Создайте файл .proto общих служб с помощью protobuf-net.Grpc
Я пытаюсь создать .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;
}
Модель Todo
[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);
}
Контракт Todo
[ServiceContract]
public interface ITodoService : IBaseService<Todo>
{
// FIND
[OperationContract]
ValueTask<Todo> GetOneByQueryAsync(Query query, CallContext context = default);
}
Используя этот общий подход, я пытаюсь предотвратить повторение кода.
- Startup.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
, Мне не хватает двух других функций из базового контракта. Также тип возвращаемого значения функции
rpc GetOneByQuery (Query) returns (Base);
неправильно, это должно быть Todo.
Какие-либо предложения?
2 ответа
Также тип возвращаемого значения функции rpc GetOneByQuery (Query) возвращает (Base); неправильно, это должно быть Todo.
Нет, правильно; Сам protobuf не имеет понятия наследования - protobuf-net должен его подкладывать, что и делает с помощью инкапсуляции, поэтому
Base
с
oneof subtype
который имеет. В вашем случае, мы ожидаем , что вещь передается всегда будет на самом деле решить , какTodo
, но язык схем .proto не имеет синтаксиса, чтобы выразить это. Лучшее, что мы могли бы сделать здесь, - это включить дополнительный комментарий в сгенерированный .proto высказывание
// return type will always be a Todo
или похожие.
Мне не хватает двух других функций из базового контракта
наследование сервисов и общие сервисы в настоящее время здесь не очень хорошо поддерживаются; опять же, это концепции, которые не имеют соответствующей метафоры в .proto или gRPC в целом, и protobuf-net нужно было бы изобрести что-то подходящее; Я до сих пор не садился и не обдумывал любую подобную схему или ее последствия. По сути, проблема здесь в том, что контракт и имя службы используются для построения маршрута / URL-адреса; когда мы говорим об одном сервисном контракте и методе, это нормально, но когда мы говорим о наследовании сервиса и дженериках, становится намного сложнее однозначно определить, о какой сервисе вы говорите, и как это должно соотноситься между маршрутом и реализацией. (и, конечно, синтаксис .proto). Я полностью открыт для размышлений здесь - просто до сих пор это не было требованием критического пути.
Что ж, на данный момент все мои службы находятся на 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);
}
- PROGRAM.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. В