While trying to simplify our MediatR code, we have created generic Request/Response/Handler combinations for GetAll, GetSingle, etc.. functionalities. Below you can find the GetAll implementation
public class GetAll<T> where T : class
{
public class Request : IRequest<Response>
{
}
public class Response
{
public IQueryable<T> All { get; set; }
}
public class Handler : IRequestHandler<Request, Response>
{
private readonly IRepository<T> repository;
public Handler(IRepository<T> repository)
{
this.repository = repository;
}
public Response Handle(Request message)
{
return new Response
{
All = repository.GetAll()
};
}
}
}
We can't seem to register all our RequestHandlers at once using Autofac.
We are able to register a single specific type of Handler in our Autofac Module using:
builder.RegisterGeneric(typeof(GetAll<>.Handler)).AsImplementedInterfaces();
But we would like to do this for all implementations of IRequestHandler<,>
(and not just the GetAll one). We've tried this using the AsClosedTypesOf
function as suggested in the documentation:
builder.RegisterAssemblyTypes(typeof(GetAll<>.Request).Assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>));
or
builder.RegisterAssemblyTypes(typeof(GetAll<>.Request).Assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>))
.AsImplementedInterfaces();
But this yields following exception:
Autofac.Core.Registration.ComponentNotRegisteredException: 'The requested service
'MediatR.IRequestHandler<GetAll<T>.Request,GetAll<T>.Response>'
has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.'
What are we doing wrong (or forgetting) here?
You won't be able to register them at once because, as you mentioned, you have to register each of them as generics. However, there's a way for you to avoid manually keeping a list of such handlers with a bit of reflection:
var genericRequestHandlers = typeof(GetAll<>).Assembly
.ExportedTypes
.Where(x => IsGenericRequestHandler(x))
.ToArray();
foreach (var genericRequestHandler in genericRequestHandlers)
{
builder
.RegisterGeneric(genericRequestHandler)
.AsImplementedInterfaces();
}
private static bool IsGenericRequestHandler(Type t)
{
return
t.IsGenericTypeDefinition &&
t.GetInterfaces().Any(i =>
{
return
i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>);
});
}
Here's an explanation of the checks done in IsGenericRequestHandler
:
GetAll<T>.Handler
is since you can construct GetAll<int>.Handler
, GetAll<string>.Handler
, etc... out of itGetAll<T>.Handler
implements IRequestHandler<GetAll<T>.Request, GetAll<T>.Response>
, which is a generic typeIRequestHandler<,>
? in our case, the generic type definition of IRequestHandler<GetAll<T>.Request, GetAll<T>.Response>
is IRequestHandler<,>
so the type matches the necessary criteria.I hope this makes sense.