.NET Core依赖项注入,解析通用接口

时间:2018-11-09 19:03:04

标签: c# generics dependency-injection .net-core

我在使用asp.net核心依赖项注入时遇到麻烦,无法从IServiceProvider解析通用接口。这是我的设置:

通用接口:

public interface IRequest<out TResponse> {...}

public interface IRequestHandler<TRequest, TResult> 
    where TRequest : IRequest<TResult> {...}

具体实现:

public class GetUsersQuery : IRequest<IEnumerable<GetUsersResult>> {...}

public abstract class RequestHandler<TRequest, TResult> 
    : IRequestHandler<TRequest, TResult>
    where TRequest : IRequest<TResult> {...}

public class GetUsersQueryHandler
    : RequestHandler<GetUsersQuery, IEnumerable<GetUsersResult>> {...}

然后我有一个服务工厂,就是我这样注册依赖项注入:

public static void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IRequestHandler<GetUsersQuery, 
    IEnumerable<GetUsersResult>>, GetUsersQueryHandler>();
}

我可以像这样成功解决我的处理程序:

var handler = 
_services.GetService<IRequestHandler<GetUsersQuery, IEnumerable<GetUsersResult>>>();

但是,我想在这个工厂中有一个通用方法,该方法可以接收IRequest的具体实现,并在不事先知道确切类型的情况下返回适当的处理程序,如下所示:

public Task<TResult> Execute<TResult>(IRequest<TResult> request)
{
    var handler =
        _services.GetService<IRequestHandler<IRequest<TResult>, TResult>>();
    return handler.ExecuteAsync(request);
}

并像这样调用此方法:

_serviceFactory.Execute(new GetUsersQuery(){});

不幸的是,这不起作用,处理程序未解析,并且为null。我觉得这应该是可能的。

您能告诉我我做错了什么以及如何实现吗?

2 个答案:

答案 0 :(得分:0)

我想我找到了一种方法,可以这样声明execute方法:

public Task<TResult> Execute<TRequest, TResult>(TRequest request)
        where TRequest : IRequest<TResult> 
{
    var handler = _services.GetService<IRequestHandler<TRequest, TResult>>();
    return handler.ExecuteAsync(request);
}

并以此方式使用:

_serviceFactory.Execute<GetUsersQuery, IEnumerable<GetUsersResult>>(query);

这有点难看,因为我必须同时指定Execute方法的请求类型和结果类型,只使用_serviceFactory.Execute(query)会更好,但我想可能不可能吗?

答案 1 :(得分:0)

此设计可能源自this博客文章。同一篇博文显示了您的确切问题的解决方案:

public TResult Process<TResult>(IQuery<TResult> query)
{
    var handlerType = typeof(IQueryHandler<,>)
        .MakeGenericType(query.GetType(), typeof(TResult));

    dynamic handler = container.GetInstance(handlerType);

    return handler.Handle((dynamic)query);
}

根据您的情况,这可以翻译为以下内容:

public Task<TResult> Execute<TResult>(IRequest<TResult> request)
{
    var handlerType = typeof(IRequestHandler<,>)
        .MakeGenericType(request.GetType(), typeof(TResult));

    dynamic handler = _services.GetRequiredService(handlerType);

    return handler.ExecuteAsync((dynamic)query);
}

关于dynamic的使用,博客文章指出:

  

不幸的是,我们需要使用反射调用Handle方法(在这种情况下,使用C#4.0 dymamic关键字),因为此时无法转换处理程序实例,因为通用TQuery参数在编译时不可用