1

我正在尝试生成此结构的 .proto :

-- 模型 --
基本模型

[DataContract]
public abstract class Base
{
   [ProtoMember(1)]
   public string Id { get; set; }

   [ProtoMember(2, DataFormat = DataFormat.WellKnown)]
   public DateTime CreatedDate { get; private set; } = DateTime.UtcNow;

   [ProtoMember(3, DataFormat = DataFormat.WellKnown)]
   public DateTime UpdatedDate { get; set; } = DateTime.UtcNow;
}         

待办模型

[ProtoContract]
public class Todo : Base
{
   [ProtoMember(1)]
   public string Title { get; set; }

   [ProtoMember(2)]
   public string Content { get; set; }
 
   [ProtoMember(3)]
   public string Category { get; set; }
}      

加上这一行:

RuntimeTypeModel.Default[typeof(Base)].AddSubType(42, typeof(Todo));

-- 合同 --
基础合同

[ServiceContract]
public interface IBaseService<T>
{
   // CREATE
   [OperationContract]
   Task<RStatus> CreateOneAsync(T request,CallContext context = default);
   
   // FIND
   [OperationContract]
   ValueTask<T> GetById(UniqueIdentification request,CallContext context = default);
}        

待办合同

[ServiceContract]
public interface ITodoService : IBaseService<Todo>
{
   // FIND        
   [OperationContract]
   ValueTask<Todo> GetOneByQueryAsync(Query query, CallContext context = default);
}          

使用这种通用方法,我试图防止重复代码。

-- 启动.cs --

     ...
endpoints.MapGrpcService<TodoService>();
endpoints.MapCodeFirstGrpcReflectionService();
     ...       

所以,当我运行这个:

var schema = generator.GetSchema<ITodoService>();

我在 .proto 文件中得到这个输出:

syntax = "proto3";
package Nnet.Contracts;
import "google/protobuf/timestamp.proto";

message Base {
   string Id = 1;
   .google.protobuf.Timestamp CreatedDate = 2;
   .google.protobuf.Timestamp UpdatedDate = 3;
   oneof subtype {
     Todo Todo = 42;
   }
}
message IEnumerable_Todo {
   repeated Base items = 1;
}
message Query {
   string Filter = 1;
}
message Todo {
   string Title = 1;
   string Content = 2;
   string Category = 3;
}
service TodoService {
   rpc GetOneByQuery (Query) returns (Base);
}
    

在 .proto 文件部分service Todoservice,我缺少 Base 合约中的其他两个函数。还有,函数的返回类型rpc GetOneByQuery (Query) returns (Base);不对,应该是Todo。

有什么建议么?

4

2 回答 2

1

另外,函数 rpc GetOneByQuery (Query) 的返回类型返回 (Base);错了,应该是Todo。

不,这是正确的;protobuf 本身没有继承的概念- protobuf-net 必须填充它,它使用封装来实现,因此Basewith aoneof subtype具有Todo. 在您的情况下,我们希望传递的东西实际上总是解析a Todo,但是 .proto 模式语言没有语法来表达这一点。我们在这里可以做的最好的事情是在生成的 .proto 语句// return type will always be a Todo或类似语句中包含一个额外的注释。

我缺少基本合同中的其他两个功能

服务继承和通用服务目前在这里没有得到很好的支持;同样,这些概念在 .proto 或 gRPC 中通常没有匹配的隐喻,protobuf-net 需要发明一些合适的东西;迄今为止,我还没有坐下来思考过任何这样的计划或其中的含义。从根本上说,这里的问题是服务合约和名称用于构造路由/url;在谈论单个服务合同和方法时,这很好 - 但是在谈论服务继承和泛型时,唯一地识别您正在谈论的服务以及应该如何在路由和实现之间映射变得更加复杂(事实上​​,.proto 语法)。我对这里的想法完全持开放态度——迄今为止,它还不是关键路径要求。

于 2021-09-13T06:25:18.667 回答
0

好吧,目前我所有的服务都在 C# 中,但如果我们必须将 .proto 文件共享给 C# 应用程序以外的其他人,那么将来支持这种接口继承模式会很棒。我不确定粘贴可以帮助您支持此功能的代码是否可以...所以,这是代码:
我正在跳过模型
-合同-

//Base Contract
public interface IBaseService<T>
{
    Task<RStatus> CreateOne(T request);

    ValueTask<T> GetById(UniqueIdentification request);
}

//Product Contract
public interface IProductService<T> : IBaseService<T>
{
    Task<T> GetByBrandName(string request);

    ValueTask<T> GetByName(string request);
}

    //Audio Contract
public interface IAudioService<T>
{
    Task<T> GetBySoundQuality(int request);
}


//Headphones Contract
public interface IHeadphonesService : IProductService<Headphones>, IAudioService<Headphones>
{
    Task<Headphones> GetByBluetoothOption(bool request);
}             

-- 程序.CS --

static void Main(string[] args)
{
    foreach (var type in TypesToGenerateForType(typeof(IHeadphonesService)))
    {
        Console.WriteLine($"Type: {type} \n");
    }
}

public static IEnumerable<Type> TypesToGenerateForType(Type type)
{
    foreach (var interfaceType in type.FindInterfaces((ignored, data) => true, null))
    {
        foreach (var dm in interfaceType.GetMethods())
        {
            Console.WriteLine($"Method Name: {dm}");
        }
        yield return interfaceType;
    }
    foreach (var tm in type.GetMethods())
    {
        Console.WriteLine($"Method Name: {tm}");
    }
    yield return type;
}      
    

输出:

Method Name: System.Threading.Tasks.Task`1[TestIntInh.Shared.Models.Headphones] GetByBrandName(System.String)
Method Name: System.Threading.Tasks.ValueTask`1[TestIntInh.Shared.Models.Headphones] GetByName(System.String)
Type: TestIntInh.Shared.Contracts.IProductService`1[TestIntInh.Shared.Models.Headphones 

Method Name: System.Threading.Tasks.Task`1[TestIntInh.Shared.Models.RStatus] CreateOne(TestIntInh.Shared.Models.Headphones)
Method Name: System.Threading.Tasks.ValueTask`1[TestIntInh.Shared.Models.Headphones] GetById(TestIntInh.Shared.Models.UniqueIdentification)
Type: TestIntInh.Shared.Contracts.IBaseService`1[TestIntInh.Shared.Models.Headphones] 

Method Name: System.Threading.Tasks.Task`1[TestIntInh.Shared.Models.Headphones] GetBySoundQuality(Int32)
Type: TestIntInh.Shared.Contracts.IAudioService`1[TestIntInh.Shared.Models.Headphones] 

Method Name: System.Threading.Tasks.Task`1[TestIntInh.Shared.Models.Headphones] GetByBluetoothOption(Boolean)
Type: TestIntInh.Shared.Contracts.IHeadphonesService        
    

我认为有了这个你可以构建适当的 .proto 文件。为您提供FindInterfaces了几乎所有您可能需要的东西。

于 2021-09-13T19:59:14.883 回答