如何在 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.SearchQuery
1[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>
的泛型类型参数,即 TRequest
和 TResponse
,并不完全映射到 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
的调用将完全失败,因为:
- 提供的类型数量不正确。
requestedAbstraction.GetGenericTypeArguments()
returns 两种,而 registeredOpenImplementation
有一种。
- 类型不匹配,因为
SearchQueryHandler<T>
上的类型限制。其中 SearchQuery<Customer>
是从抽象中提取的,SearchQueryHandler<T>
期望 Customer
.
长话短说,MS.DI 不是为您的用例构建的。您有两个选择:
- 切换到不同的 DI 容器。有成熟的 DI 容器支持这些类型的注册。我无法为您提供支持您要求的所有 DI 容器的完整列表,但我确信 Autofac 和 Simple Injector 对泛型有很好的支持。
- 远离这种批量注册并手动注册所有封闭的通用类型。例如:
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
我有以下class
SearchQuery<TEntity>
它在控制器上像这样与 MediatR 一起使用:
await Mediator.Send(
new SearchQuery<TEntity>(
new PageInformation(pageNumber, pageSize), Request.Query));
并且我已经在我的应用程序中的服务集合中注册了 MediatR,如下所示:
services.AddMediatR(Assembly.GetExecutingAssembly());
SearchQuery
class 位于正确的命名空间中,它与已创建的所有其他命名空间位于同一位置,但是当我 运行 应用程序时,我收到以下错误。
MediatR.IRequestHandler
2[AssetManagement.Core.Application.Search.Queries.SearchQuery
1[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>
的泛型类型参数,即 TRequest
和 TResponse
,并不完全映射到 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
的调用将完全失败,因为:
- 提供的类型数量不正确。
requestedAbstraction.GetGenericTypeArguments()
returns 两种,而registeredOpenImplementation
有一种。 - 类型不匹配,因为
SearchQueryHandler<T>
上的类型限制。其中SearchQuery<Customer>
是从抽象中提取的,SearchQueryHandler<T>
期望Customer
.
长话短说,MS.DI 不是为您的用例构建的。您有两个选择:
- 切换到不同的 DI 容器。有成熟的 DI 容器支持这些类型的注册。我无法为您提供支持您要求的所有 DI 容器的完整列表,但我确信 Autofac 和 Simple Injector 对泛型有很好的支持。
- 远离这种批量注册并手动注册所有封闭的通用类型。例如:
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