I have the following class
SearchQuery<TEntity>
And it's used with MediatR like this on a controller:
await Mediator.Send(
new SearchQuery<TEntity>(
new PageInformation(pageNumber, pageSize), Request.Query));
And I've registered MediatR with the service collection in my app as follows:
services.AddMediatR(Assembly.GetExecutingAssembly());
The SearchQuery
class is in the correct namespace, it's in the same place as every other one which has been created, but when I run the application, I receive the following error.
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
The SearchQuery<T>
class in detail:
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?
...
}
}
I've tried to register the type with the service collection as below
services.AddTransient(
typeof(IRequestHandler<,>),
typeof(SearchQueryHandler<TEntity>));
But then receive the following error:
System.ArgumentException: 'Open generic service type 'MediatR.IRequestHandler`2[TRequest,TResponse]' requires registering an open generic implementation type
I have tried various other ways of resolving this, but I feel I'm going around in circles.
What you are trying to achieve is not possible with MS.DI. You are trying to map the open-generic interface IRequestHandler<TRequest, TResponse>
to an open-generic implementation SearchQueryHandler<T>
. But with your desired mapping, the generic type arguments of IRequestHandler<TRequest, TResponse>
, namely TRequest
and TResponse
, do not exactly map to that of SearchQueryHandler<T>
.
When it comes to generics, MS.DI is very simplistic. To be able to make a mapping, MS.DI must be able to fill in the generic type arguments of the abstraction directly into the created implementation. To make it less abstract, MS.DI simply calls .MakeGenericType()
similar to the following code:
Type registeredOpenImplementation = typeof(SearchQueryHandler<>);
Type requestedAbstraction =
typeof(IRequestHandler<SearchQuery<Customer>, PaginatedList<Customer>>);
Type closedImplementationToResolve =
registeredOpenImplementation
.MakeGenericType(requestedAbstraction.GetGenericTypeArguments());
The call to MakeGenericType
, however, would completely fail, because:
requestedAbstraction.GetGenericTypeArguments()
returns two types, while registeredOpenImplementation
has one.SearchQueryHandler<T>
. Where SearchQuery<Customer>
is extracted from the abstraction, SearchQueryHandler<T>
expects Customer
.Long story short, MS.DI is not built for your use case. You have two options:
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