首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Protobuf-net.Grpc服务合同继承

Protobuf-net.Grpc服务合同继承
EN

Stack Overflow用户
提问于 2020-05-11 18:47:31
回答 1查看 919关注 0票数 1

我正在将一个应用程序从.Net固件升级到.Net核心。同时将WCF服务升级到gRPC服务。我们决定使用protobuf-net.Grpc。

我们对服务合同使用多级继承。

代码语言:javascript
复制
[ServiceContract]
public interface IBaseServiceContract<TDataModel> where TDataModel : DataModelObject
{
}

[ServiceContract]
public interface ICRUDLServiceContract<TDataModel> : IBaseServiceContract<TDataModel> where TDataModel : DataModelObject
{
    Task<Response<TDataModel>> Create(Request<TDataModel> request);
    Task<Response<TDataModel>> Read(Request<TDataModel> request);
    Task<VoidResponse> Update(Request<TDataModel> request);
    Task<VoidResponse> Delete(Request<TDataModel> request);
    Task<Response<DataResult<TDataModel>>> List(Request<DataSourceQuery> request);
    Task<Response<IEnumerable<TDataModel>>> ListAll();
}

一个简单的服务合同看起来像这样:

代码语言:javascript
复制
[ServiceContract]
public interface IProductService : ICRUDLServiceContract<Product>
{
}

在我们的控制器层中,使用服务层,我们有通用控制器,它们通过基本通用接口使用这些服务契约:

代码语言:javascript
复制
[ApiController]
public class CRUDLController<TDataModel, TServiceContract, TViewModel> : CRUDController<TDataModel, TServiceContract, TViewModel>
    where TDataModel : DataModelObject, new()
    where TServiceContract : ICRUDLServiceContract<TDataModel>
    where TViewModel : ViewModelObject, new()
{

    public CRUDLController(ILogger<BaseController<TDataModel, TServiceContract, TViewModel>> logger, TServiceContract dataService, IMapper mapper)
        : base(logger, dataService, mapper)
    {
    }

    [HttpGet]
    [Route("ListAll")]
    public virtual async Task<ActionResult<IEnumerable<TViewModel>>> ListAll()
    {
        try
        {
            var response = await _dataService.ListAll();
            return _mapper.Map<IEnumerable<TViewModel>>(response.Result).ToList();
        }
        catch (Exception ex)
        {
            throw CreateUserException(ex, MethodBase.GetCurrentMethod().Name);
        }
    }

    [HttpGet]
    [ActionName("List")]
    public virtual async Task<ActionResult<DataSourceResult>> List([ModelBinder(typeof(DataSourceQueryModelBinder))] DataSourceQuery query)
    {
        try
        {
            var response = await _dataService.List(query.AsRequest());
            return response.MapToDataSourceResult<TDataModel, TViewModel>(_mapper);
        }
        catch (Exception ex)
        {
            throw CreateUserException(ex, MethodBase.GetCurrentMethod().Name);
        }
    }

        ...
}

在服务应用程序的Startup.cs中,我们将所有服务映射为:

代码语言:javascript
复制
...
endpoints.MapGrpcService<ProductService>();
endpoints.MapGrpcService<TitleService>();
...

当我启动服务应用程序时,我在控制台日志上看到错误:

代码语言:javascript
复制
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
      Request starting HTTP/2 POST https://localhost:7001/OverBase.Core.Contract.CRUDLServiceContract`1/List application/grpc -
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
      An unhandled exception has occurred while executing the request.
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches:

gRPC - /OverBase.Core.Contract.CRUDLServiceContract`1/List
gRPC - /OverBase.Core.Contract.CRUDLServiceContract`1/List

然后,我尝试从基接口中删除[ServiceContract]

代码语言:javascript
复制
warn: Grpc.AspNetCore.Server.Model.Internal.ServiceRouteBuilder[3]
No gRPC methods discovered for OverBase.Services.Program.ProductService.

基接口中的方法已经从服务中消失了。

有没有办法在protobuf-net.Grpc中使用基接口中的方法?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-05-12 10:20:04

在github上报告这将是一个很好的bug。问题是约定绑定器不会展开泛型,因此最终会有多个具有相同名称的服务,并带有.NET泛型占位符(但内部API不同):

代码语言:javascript
复制
IFoo`1/Method
IFoo`1/Method

从长远来看,我们应该修复这个库。使用属性来修复这个问题并不容易,因为属性不是针对每个T的,但是:我们可以编写自己的绑定器:

代码语言:javascript
复制
    // note: this could also simply recognize a few known interfaces
    class MyServiceBinder : ServiceBinder
    {
        protected override string GetDefaultName(Type contractType)
        {
            var val = base.GetDefaultName(contractType);
            if (val.EndsWith("`1") && contractType.IsGenericType)
            {   // replace IFoo`1 with IFoo`TheThing
                var args = contractType.GetGenericArguments();
                if (args.Length == 1)
                {
                    val = val.Substring(0, val.Length - 1) + args[0].Name;
                }
            }
            return val;
        }
    }

对于ASP.NET,我们将其注册到DI:

代码语言:javascript
复制
services.AddSingleton(BinderConfiguration.Create(binder: new MyServiceBinder()));

对于客户端,您可以将其传递给客户端创建者:

代码语言:javascript
复制
static readonly ClientFactory s_ClientFactory = ClientFactory.Create(
    BinderConfiguration.Create(binder: new MyServiceBinder()));
// ...
var calculator = http.CreateGrpcService<IWhatever>(s_ClientFactory);

这样做的结果是我们绑定到:

代码语言:javascript
复制
IFoo`X/Method
IFoo`Y/Method

显然,你可以随意提出不同的模式!gRPC并不太关心它们是什么。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61727870

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档