如何在 MediatR 中注册通用类型的请求

Jam*_*mes 8 c# dependency-injection mediatr asp.net-core

我有以下课程

SearchQuery<TEntity>
Run Code Online (Sandbox Code Playgroud)

它与 MediatR 一起在控制器上使用,如下所示:

await Mediator.Send(
    new SearchQuery<TEntity>(
        new PageInformation(pageNumber, pageSize), Request.Query));
Run Code Online (Sandbox Code Playgroud)

我已在我的应用程序中将 MediatR 注册到服务集合中,如下所示:

services.AddMediatR(Assembly.GetExecutingAssembly());
Run Code Online (Sandbox Code Playgroud)

该类SearchQuery位于正确的命名空间中,它与已创建的所有其他类位于同一位置,但是当我运行该应用程序时,我收到以下错误。

MediatR.IRequestHandler 2[AssetManagement.Core.Application.Search.Queries.SearchQuery1[AssetManagement.Core.Domain.Entities.TEntity]、AssetManagement.Core.Application.Models.PaginatedList`1[AssetManagement.Core.Domain.Entities.TEntity]]。向容器注册您的处理程序。有关示例,请参阅 GitHub 中的示例

SearchQuery<T>详细班级:

public class SearchQuery<T> : IRequest<PaginatedList<T>>
{
    public SearchQuery(
        PageInformation pageInformation,
        IQueryCollection searchQueryCollection)
    {
        QueryCollection = searchQueryCollection;
        PageInformation = pageInformation;
    }
    public IQueryCollection QueryCollection { get; }
    public PageInformation PageInformation { get; }
}

public class SearchQueryHandler<T>
    : IRequestHandler<SearchQuery<T>, PaginatedList<T>>
{
    private readonly IFormsDbContext _formsContext;

    public SearchQueryHandler(IFormsDbContext formsContext)
    {
        _formsContext = formsContext;
    }

    public Task<PaginatedList<T>> Handle(
        SearchQuery<T> request, CancellationToken cancellationToken)
    {
        //return PaginatedList<Entity> here?
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用服务集合注册类型,如下所示

services.AddTransient(
    typeof(IRequestHandler<,>),
    typeof(SearchQueryHandler<TEntity>));
Run Code Online (Sandbox Code Playgroud)

但随后收到以下错误:

System.ArgumentException:'开放通用服务类型'MediatR.IRequestHandler`2 [TRequest,TResponse]'需要注册开放通用实现类型

我尝试过各种其他方法来解决这个问题,但我觉得我在兜圈子。

Ste*_*ven 6

MS.DI 无法实现您想要实现的目标。您正在尝试将开放式通用接口映射IRequestHandler<TRequest, TResponse>到开放式通用实现SearchQueryHandler<T>。但是,根据您所需的映射, 的泛型类型参数IRequestHandler<TRequest, TResponse>(即TRequestTResponse)并不完全映射到 的泛型类型参数SearchQueryHandler<T>

当谈到仿制药时,MS.DI 非常简单。为了能够进行映射,MS.DI 必须能够将抽象的通用类型参数直接填充到创建的实现中。为了使其不那么抽象,MS.DI 只需调用.MakeGenericType()类似于以下代码:

Type registeredOpenImplementation = typeof(SearchQueryHandler<>);

Type requestedAbstraction =
    typeof(IRequestHandler<SearchQuery<Customer>, PaginatedList<Customer>>);

Type closedImplementationToResolve =
    registeredOpenImplementation
        .MakeGenericType(requestedAbstraction.GetGenericTypeArguments());
Run Code Online (Sandbox Code Playgroud)

MakeGenericType然而,对 的调用将完全失败,因为:

  1. 提供的类型数量不正确。requestedAbstraction.GetGenericTypeArguments()返回两种类型,而registeredOpenImplementationhas 一种。
  2. 由于 的类型限制,类型不匹配SearchQueryHandler<T>。哪里SearchQuery<Customer>是从抽象中提取的,SearchQueryHandler<T>期望Customer

长话短说,MS.DI 并不是为您的用例而构建的。您有两个选择:

  1. 切换到不同的 DI 容器。有成熟的 DI 容器支持此类注册。我无法为您提供支持您的要求的所有 DI 容器的完整列表,但我确信 Autofac 和 Simple Injector 对泛型有很好的支持。
  2. 远离这种批量注册并手动注册所有封闭通用类型。例如:
    services.AddTransient<
       IRequestHandler<SearchQuery<Customer>, PaginatedList<Customer>>,
       SearchQueryHandler<Customer>>();
    services.AddTransient<
       IRequestHandler<SearchQuery<Order>, PaginatedList<Order>>,
       SearchQueryHandler<Order>>();
    services.AddTransient<
       IRequestHandler<SearchQuery<Product>, PaginatedList<Product>>,
       SearchQueryHandler<Product>>();
    services.AddTransient<
       IRequestHandler<SearchQuery<Employee>, PaginatedList<Employee>>,
       SearchQueryHandler<Employee>>();
    // etc
    
    Run Code Online (Sandbox Code Playgroud)