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

How do I register a request with a generic type in MediatR

我有以下class

SearchQuery<TEntity>

它在控制器上像这样与 MediatR 一起使用:

await Mediator.Send(
    new SearchQuery<TEntity>(
        new PageInformation(pageNumber, pageSize), Request.Query));

并且我已经在我的应用程序中的服务集合中注册了 MediatR,如下所示:

services.AddMediatR(Assembly.GetExecutingAssembly());

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

MediatR.IRequestHandler2[AssetManagement.Core.Application.Search.Queries.SearchQuery1[AssetManagement.Core.Domain.Entities.TEntity],AssetManagement.Core.Application.Models.PaginatedList`1[AssetManagement.Core.Domain.Entities.TEntity]]. Register your handlers with the container. See the samples in GitHub for examples

SearchQuery<T> class 详细:

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?
        ...
    }
}

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

services.AddTransient(
    typeof(IRequestHandler<,>),
    typeof(SearchQueryHandler<TEntity>));

但随后收到以下错误:

System.ArgumentException: 'Open generic service type 'MediatR.IRequestHandler`2[TRequest,TResponse]' requires registering an open generic implementation type

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

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());

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

  1. 提供的类型数量不正确。 requestedAbstraction.GetGenericTypeArguments() returns 两种,而 registeredOpenImplementation 有一种。
  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