Using simple injector with the command pattern described here. Most commands have companion classes that implement fluent validation's AbstractValidator<TCommand>
, which means they also implement FV IValidator<TCommand>
. However it doesn't always make sense to have a validator implementation for every command.
As far as I can tell, the command decorator implementation cannot take IValidator<TCommand>
as a constructor arg unless every ICommandHandler<TCommand>
has a corresponding FV.IValidator<TCommand>
. I tried the following:
public class FluentValidationCommandDecorator<TCommand>
: IHandleCommands<TCommand>
{
public FluentValidationCommandDecorator(IHandleCommands<TCommand> decorated
, IValidator<TCommand> validator
)
{
_decorated = decorated;
_validator = validator;
}
...
}
...
container.RegisterManyForOpenGeneric(typeof(IValidator<>), assemblies);
container.RegisterDecorator(typeof(IHandleCommands<>),
typeof(FluentValidationCommandDecorator<>),
context =>
{
var validatorType =
typeof (IValidator<>).MakeGenericType(
context.ServiceType.GetGenericArguments());
if (container.GetRegistration(validatorType) == null)
return false;
return true;
});
Unit tests that run Container.Verify()
once, pass. Unit tests that run Container.Verify()
more than once, fail from an InvalidOperationException
on the second invocation:
The configuration is invalid. Creating the instance for type
IValidator<SomeCommandThatHasNoValidatorImplementation> failed. Object reference
not set to an instance of an object.
The following works, by taking the Container
as an argument:
public class FluentValidationCommandDecorator<TCommand>
: IHandleCommands<TCommand>
{
private readonly IHandleCommands<TCommand> _decorated;
private readonly Container _container;
public FluentValidationCommandDecorator(Container container
, IHandleCommands<TCommand> decorated
)
{
_container = container;
_decorated = decorated;
}
public void Handle(TCommand command)
{
IValidator<TCommand> validator = null;
if (_container.GetRegistration(typeof(IValidator<TCommand>)) != null)
validator = _container.GetInstance<IValidator<TCommand>>();
if (validator != null) validator.ValidateAndThrow(command);
_decorated.Handle(command);
}
}
...
container.RegisterManyForOpenGeneric(typeof(IValidator<>), assemblies);
container.RegisterDecorator(typeof(IHandleCommands<>),
typeof(FluentValidationCommandDecorator<>));
If this class didn't have to take a dependency on Simple Injector, I could move it into the domain project. The domain already takes a dependency on FluentValidation.net so that domain validity can be unit tested. I think this decorator belongs in the domain, but neither it nor its unit test project takes a dependency on simpleinjector (or should have to, since the domain is not the composition root).
Is there a way to tell simpleinjector to only decorate a CommandHandler<TCommand>
instance with a FluentValidationCommandDecorator<TCommand>
if there is an implementation registered for IValidator<TCommand>
?
What you need is unregistered type resolution, to map missing types to a default implementation. Or in other words, you need to use the RegisterOpenGeneric
method:
container.RegisterOpenGeneric(typeof(IValidator<>),
typeof(NullValidator<>));
Now you need to define a NullValidator<T>
that implements IValidator<T>
with an empty / default implementation.
When you do this every time a certain (unregistered) IValidator<T>
is requested, a new instance of NullValidator<T>
will be returned. It will not override types that are registered using RegisterManyForOpenGeneric
, since those types are explictly registered.
When the NullValidator<T>
implementation is thread-safe (which will usually be the case with a empty implementation), you can optimize construction by registering them as singleton:
container.RegisterSingleOpenGeneric(typeof(IValidator<>),
typeof(NullValidator<>));
You can read more information in the wiki: Registration of open generic types