I'm trying to resolve a mixed open-closed generic type with constraints using Castle Windsor. This should be able to resolve any open generics if Foo implements IFoo<>:
container.Register(Component.For(typeof(IFoo<>).ImplementedBy(typeof(Foo<>)));
My situation is slightly more complex:
I have the following Handler
class:
public abstract class CommandHandlerBase<TCommand, TResponse>
: IRequestHandler<TCommand, TResponse>
where TCommand : IRequest<TResponse>
{
public abstract Task<TResponse> Handle(
TCommand request, CancellationToken cancellationToken);
}
public class AddMasterDataEntityCommandHandler<TNewEntityData>
: IRequestHandler<TNewEntityData, Response>
where TNewEntityData : IRequest<Response>
{
public Task<Response> Handle(
TNewEntityData request, CancellationToken cancellationToken)
{
// ...
}
}
The idea is that AddMasterDataEntityCommandHandler
will be a generic command handler that can handle any type of contract of type TNewEntityData
.
Since I'm using Mediatr, my contracts have to implement IRequest<,>
, which they do. In this case, I'm enforcing that all the handlers should return a Response
.
Example usage:
Response response = await mediator.Send(new AddMasterDataEntityCommand<NewPlace>());
I created a simple console app to isolate this behavior:
public static void Main(string[] args)
{
var container = new WindsorContainer();
container.Register(Types.FromThisAssembly()
.BasedOn(typeof(IRequestHandler<,>))
.Unless(t => t.IsAbstract || t.IsInterface)
.WithServices(typeof(IRequestHandler<,>))
.LifestyleTransient());
var instance = container.Resolve(typeof(IRequestHandler<NewData, Response>));
}
However, a test throws an exception indicating an error in my code:
Castle.MicroKernel.Handlers.GenericHandlerTypeMismatchException: 'Types ConsoleApp4.NewData, ConsoleApp4.Response don't satisfy generic constraints of implementation type ConsoleApp4.AddMasterDataEntityCommandHandler'1 of component 'ConsoleApp4.AddMasterDataEntityCommandHandler'1'. This is most likely a bug in your code.'
I don't see the issue here, CW should be able to resolve open/closed generics, right? Furthermore, the problem seems to be related to the additional Response
parameter as a TResponse
type. Did I register the component the wrong way? I'm confident I didn't mess up the generic constraints...
Thanks in advance to anyone who can take a look.
Krzysztof Kozmic led me into the right direction:
I initially tried an implementation with IGenericImplementationMatchingStrategy
but couldn't get this working to it only being able to handle a type with one generic. I ended up registering like this with a bit of reflection magic:
private void RegisterGenericMasterDataCommandHandlers(IWindsorContainer container) {
foreach (Type contractType in contractTypes) {
Type requestedType = typeof(IRequestHandler<,>).MakeGenericType(typeof(AddMasterDataEntityCommand<>).MakeGenericType(contractType), typeof(Response));
Type implementedType = typeof(AddMasterDataEntityCommandHandler<>).MakeGenericType(contractType);
container.Register(Component.For(requestedType, implementedType));
}
}