I am trying to make use of some of the nice features in simple injector.
I am currently having problems with the decorators, they are not getting hit when I expect them too.
I am registering them like this:
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(CreateValidFriendlyUrlCommandHandler<>),
context => context.ServiceType == typeof(ICommandHandler<CreateProductCommand>)
);
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(CreateProductValidationCommandHandler<>),
context => context.ServiceType == typeof(ICommandHandler<CreateProductCommand>)
);
I think I must be missing something as I am expecting that a call to ICommandHandler<CreateProductCommand>
will invoke CreateValidFriendlyUrlCommandHandler<>
and CreateProductValidationCommandHandler<>
before running itself.
I have tried a different registration like this:
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(CreateValidFriendlyUrlCommandHandler<>),
context => context.ImplementationType == typeof(CreateProductCommandHandler)
);
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(CreateProductValidationCommandHandler<>),
context => context.ImplementationType == typeof(CreateProductCommandHandler)
);
As I thought registering a decorator for ICommandHandler<CreateProductCommand>
on the type ICommandHandler<CreateProductCommand>
when the CreateProductValidationCommandHandler
and CreateValidFriendlyUrlCommandHandler
implement ICommandHandler<CreateProductCommand>
may well hit a bit of a circular reference.
But changing that made no difference.
Here is my CreateProductValidationCommandHandler<TCommand>
:
public class CreateProductValidationCommandHandler<TCommand>
: ICommandHandler<CreateProductCommand>
{
private readonly ICommandHandler<TCommand> decorated;
private readonly IValidationService validationService;
public CreateProductValidationCommandHandler(
ICommandHandler<TCommand> decorated,
IValidationService validationService)
{
this.decorated = decorated;
this.validationService = validationService;
}
public void Handle(CreateProductCommand command)
{
if (!validationService.IsValidFriendlyName(
command.Product.ProductFriendlyUrl))
{
command.ModelStateDictionary.AddModelError(
"ProductFriendlyUrl",
"The Friendly Product Name is not valid...");
return;
}
if (!validationService.IsUniqueFriendlyName(
command.Product.ProductFriendlyUrl))
{
command.ModelStateDictionary.AddModelError(
"ProductFriendlyUrl",
"The Friendly Product Name is ...");
return;
}
}
}
And this is my CreateValidFriendlyUrlCommandHandler<TCommand>
:
public class CreateValidFriendlyUrlCommandHandler<TCommand>
: ICommandHandler<CreateProductCommand>
{
private readonly ICommandHandler<TCommand> decorated;
public CreateValidFriendlyUrlCommandHandler(ICommandHandler<TCommand> decorated)
{
this.decorated = decorated;
}
public void Handle(CreateProductCommand command)
{
if (string.IsNullOrWhiteSpace(
command.Product.ProductFriendlyUrl))
{
command.Product.ProductFriendlyUrl =
MakeFriendlyUrl(command.Product.Name);
}
}
}
The problem is that Simple Injector will never be able to wrap an ICommandHandler<T>
implementation with one of your decorators, because there is an unresolvable generic type TCommand
. You would have noticed this if the Handle
method of your decorators would call the decorated
instance. For instance:
public class CreateValidFriendlyUrlCommandHandler<TCommand>
: ICommandHandler<CreateProductCommand>
{
private readonly ICommandHandler<TCommand> decorated;
public CreateValidFriendlyUrlCommandHandler(
ICommandHandler<TCommand> decorated)
{
this.decorated = decorated;
}
public void Handle(CreateProductCommand command)
{
// This won't compile since CreateProductCommand and
// TCommand are not related.
this.decorated.Handle(command);
}
}
This code won't compile, since the decorator's Handle
method takes an CreateProductCommand
argument, while the decorated
instance takes a TCommand
argument, which isn't specified (and nowhere is stated that CreateProductCommand
is a TCommand
).
In fact you didn't create a decorator at all. A decorator wraps an instance of the same interface that it implements. You wrap an ICommandHandler<TCommand>
while you implement an ICommandHandler<CreateProductCommand>
. The only way you would get this to work is when you explicitly specify the TCommand
to be a CreateProductCommand
, as follows:
ICommandHandler<CreateProductCommand> handler =
new CreateValidFriendlyUrlCommandHandler<CreateProductCommand>(
new CreateProductCommandHandler()
);
Still, there is no way for Simple Injector to 'guess' that this TCommand
should be a CreateProductCommand
and that's why your 'decorator' didn't get wrapped.
Long story short: ditch the TCommand
:
public class CreateValidFriendlyUrlCommandHandler
: ICommandHandler<CreateProductCommand>
{
private ICommandHandler<CreateProductCommand> decorated;
public CreateValidFriendlyUrlCommandHandler(
ICommandHandler<CreateProductCommand> decorated)
{
this.decorated = decorated;
}
public void Handle(CreateProductCommand command)
{
// logic here
}
}
Or make it generic with a type constraint:
public class CreateValidFriendlyUrlCommandHandler<TCommand>
: ICommandHandler<TCommand>
where TCommand : CreateProductCommand
{
private ICommandHandler<TCommand> decorated;
public CreateValidFriendlyUrlCommandHandler(
ICommandHandler<TCommand> decorated)
{
this.decorated = decorated;
}
public void Handle(TCommand command)
{
// logic here
}
}
or remove the type constraint and allow handling any type of command, not only CreateProductCommand
.
Note that if you are defining many decorators that can only handle one specific type of command handler, you might want to reconsider your strategy. There might be a problem in your design.