Search code examples
c#.netsimple-injector

How Can I Apply Conditional Decorator to a Command Handler with SimpleInjector?


I have the following command handler interface:

public interface ICommandHandler<TCommand> where TCommand : ICommand
{
    void Handle(TCommand command);
}

I am decorating instances of this interface with the following concrete class:

public class ValidationCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
    where TCommand : ICommand
{
     public ValidationCommandHandlerDecorator(
         IValidator<TCommand> validator, 
         ICommandHandler<TCommand> handler)
     {
     }

     public void Handle(TCommand command) {  }        
}

BUT.... I don't necessarily want to decorate ALL command handlers and only want to decorate the ICommandHandler<TCommand> IF an instance exists/is registered of IValidator<TCommand> for the concrete type of TCommand. Note that the IValidator<TCommand> instance is injected in the constructor of the decorator class.

For example, if I have a command handler:

public class CreateFooCommandHandler : ICommandHandler<CreateFooCommand>

I only want to decorate if I have the following instance registered:

public class CreateFooCommandValidator : IValidator<CreateFooCommand>

If the CreateFooCommandValidator does not exist then I don't want to decorate the CreateFooCommandHandler with the ValidationCommandHandlerDecorator.

I am using the following when registering with SimpleInjector:

var container = new Container();

container.Register(typeof(ICommandHandler<>), assemblies);
container.Register(typeof(IValidator<>), assemblies);
container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(ValidationCommandHandlerDecorator<>));

Obviously this fails if there is not instance of IValidator<> present for any given ICommandHandler<>. For info assemblies is a collection of assemblies used in for registering generic classes.

What should I use to register the decorator/validators to achieve what I want to do, if at all that is possible? I don't want to switch from using SimpleInjector.

Furthermore, if it is possible, is this recommended or is it a violation of SOLID principles, or even just a code smell?


Solution

  • You could register a conditional decorator by analysing registrations in the container and deciding whether to decorate each instance or not but I don't think it's the best option. The simplest solution is to define and register a fallback NullValidator for those instances where an actual IValidator does not exist ...

    public class NullValidator<TCommand> : IValidator<TCommand> where TCommand : ICommand
    {
        public void Validate(TCommand command)
        {
        }
    }
    

    Registered as a Conditional:

    var container = new Container();
    
    container.Register(typeof(ICommandHandler<>), assemblies);
    container.Register(typeof(IValidator<>), assemblies);
    container.RegisterConditional(
        typeof(IValidator<>), 
        typeof(NullValidator<>), 
        c => !c.Handled);
    container.RegisterDecorator(
        typeof(ICommandHandler<>), 
        typeof(ValidationCommandHandlerDecorator<>));
    
    container.Verify();
    

    I don't want to switch from using SimpleInjector.

    Good man!

    Furthermore, if it is possible, is this recommended or is it a violation of SOLID principles, or even just a code smell?

    This is exactly the kind of thing RegisterConditional exists for :-)