Search code examples
c#asp.net-mvcdecoratorcqrsstructuremap3

How to register an optional decorator or decorator with optional parameters using structuremap asp.net mvc?


I have implemented a CQRS approach in my application heavily influenced by this fantastic article: https://cuttingedge.it/blogs/steven/pivot/entry.php?id=9. My code for the commands and handlers is set it up identically to the article and that part is working well. My problem comes in when I try to implement a decorator class to handle validation of the command. The simple command handling interfaces look like this:

public interface ICommand
{
}

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

Then for the validation decorator I have:

public class ValidationCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> where TCommand : CommandBase
{
    private readonly ICommandHandler<TCommand> _decoratedCommandHandler;
    private readonly ICommandValidator<TCommand> _commandValidator;

    public ValidationCommandHandlerDecorator(ICommandHandler<TCommand> decoratedCommandHandler, ICommandValidator<TCommand> commandValidator)
    {
        _decoratedCommandHandler = decoratedCommandHandler;
        _commandValidator = commandValidator;
    }

    public void Handle(TCommand command)
    {
        if (_commandValidator != null)
        {
            var validationResult = _commandValidator.Validate(command);

            if (validationResult != null)
            {
                command.Success = false;
                command.Errors = validationResult;
                return;
            }
        }

        _decoratedCommandHandler.Handle(command);
        command.Success = true;
    }
}    

Which makes use of an interface to define the validators:

public interface ICommandValidator<TCommand>
{
    IEnumerable<string> Validate(TCommand command);
}

And CommandBase is a simple base class that allows me to store the success or failure of the command and the errors that occurred if it failed. I prefer this method as an alternative to throwing an exception. All commands would inherit this base class.

public abstract class CommandBase : ICommand
{
    public bool Success { get; set; }
    public IEnumerable<string> Errors { get; set; }
}

And this is all wired up to the IoC container within the structure map registry:

public class CommandRegistry : Registry
{
    public CommandRegistry()
    {
        Scan(s =>
        {
            s.AssemblyContainingType<CommandBase>();
            s.ConnectImplementationsToTypesClosing(typeof(ICommandHandler<>));
            s.ConnectImplementationsToTypesClosing(typeof(ICommandValidator<>));
            s.WithDefaultConventions();

            For(typeof(ICommandHandler<>)).DecorateAllWith(typeof(ValidationCommandHandlerDecorator<>));
        });
    }
}

Now, since I register that decorator for every single ICommandHandler, if I ever have a command that doesn't need a validator and doesn't define one, the ICommandValidator<TCommand> _commandValidator private field of the ValidationCommandHandlerDecorator<TCommand> class cannot be found because it of course does not exist and will always throw a structure map error of:

"No default Instance is registered and cannot be automatically determined for type 'ICommandValidator' There is no configuration specified for ICommandValidator"

Is there a way in structure map to define how the ValidationCommandHandlerDecorator gets constructed so that is uses some type of default validator when one does not exist without having to either take a dependency on the container in the class or having to create an IValidateableCommandHandler<TCommand> interface to handle commands with validators?

Thank you.


Solution

  • In case anyone comes across this later, the solution I came up with was to add a DefaultCommandValidator class as a Null Object pattern class:

    public class DefaultCommandValidator<TCommand> : ICommandValidator<TCommand> where TCommand : CommandBase
    {
        public IEnumerable<string> Validate(TCommand command)
        {
            return Enumerable.Empty<string>();
        }
    }
    

    And then add this line to the structure map registry:

    For(typeof(ICommandValidator<>)).Use(typeof(DefaultCommandValidator<>));
    

    I wasn't aware that this structure map syntax will actually use the default instance ONLY if it can't find a concrete implementation of ICommandValidator<TCommand>. Now, if I don't have a validator, I simply don't add one and the DefaultCommandValidator<TCommand> instance is used to return an empty/successful validation.