We currently use SimpleInjector as our DI container to implement the command-query-separation (CQS) pattern by using ICommand
and IQuery<TResult>
and ICommandHandler<TCommand>
and IQueryHandler<TQuery, TResult>
interfaces.
We also use the decorator pattern to perform aspect-oriented programming. For one of our decorators we use FluentValidation to perform validation logic for specific commands or queries.
With SimpleInjector, it is possible to conditionally register types. This is useful for our FluentValidation decorator when we have a command or query which does not have a corresponding validator. Then we use a NullValidator<T>
as fallback which does nothing. Exactly this scenario is described in the SimpleInjector documentation and looks like this for the FluentValidation scenario:
public class NullValidator<T> : AbstractValidator<T>
{
}
container.RegisterConditional(
typeof(IValidator<>),
typeof(NullValidator<>),
c => !c.Handled); // Applied if no other validator matches
The NullValidator<T>
is needed, since the decorator is always applied and a IValidator<T>
is always injected. For non-existing specific validators, the NullValidator<T>
is used. Here is the constructor of the FluentValidation decorator for commands:
public FluentValidationCommandHandlerDecorator(
IValidator<TCommand> validator,
ICommandHandler<TCommand> decoratee)
{
this.validator = validator ?? throw new ArgumentNullException(nameof(validator));
this.decoratee = decoratee ?? throw new ArgumentNullException(nameof(decoratee));
}
This works perfectly fine but now we are evaluating to remove the dependency to SimpleInjector and use the .NET Dependency Injection (IServiceCollection
from Microsoft.Extensions.DependencyInjection
) in combination with Scrutor for registering our ICommandHandler<T>
and IQueryHandler<TQuery, TResult>
implementations and our decorators.
Is there any way to perform a similar conditional registration logic or fallback mechanism like SimpleInjector supports in the .NET DI framework?
I managed to work around this issue by injecting an IEnumerable<IValidator<T>>
in the FluentValidationCommandHandlerDecorator
decorator instead of one single IValidator<T>
instance. In that case I don't need a NullValidator<T>
at all and an empty collection gets injected if no validator is available for the specific command or query.
SimpleInjector must be changed to register a collection of IValidator<T>
instances:
container.Collection.Register(typeof(IValidator<>), assemblies);
The Microsoft.Extensions.DependencyInjection
DI container supports injecting IEnumerable<IValidator<T>>
out-of-the box without needing to change anything in the registration.