Search code examples
c#dependency-injectionsimple-injector

RegisterCollection from previously registered types with Simple Injector


Using Simple Injector, I'm trying to register an IEnumerable of non generic ICommandHandler. This will be a collection of objects that decorate all instances of my generic ICommandHandlers. The essential code is provided below. My CommandService is the type that depends on the IEnumerable of ICommandHandler.

How do I register this in my composition root?

public interface ICommandHandler
{
    Type CommandType { get; }
    void Handle(object command);
}

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

public class WeaklyTypedCommandHandler<TCommand> : ICommandHandler,
    ICommandHandler<TCommand>
    where TCommand : ICommand
{
    private readonly ICommandHandler<TCommand> _commandHandler;

    public WeaklyTypedCommandHandler(ICommandHandler<TCommand> commandHandler) {
        _commandHandler = commandHandler;
    }

    public Type CommandType { get { return typeof (TCommand); } }

    public void Handle(object command) {
        _commandHandler.Handle((TCommand)command);
    }

    public void Handle(TCommand command) {
        _commandHandler.Handle(command);
    }
}

My CommandService:

public class CommandService : ICommandService
{
    private readonly IEnumerable<ICommandHandler> _commandHandlers; 
    private readonly ISerializationFormatter _formatter;

    public CommandService(IEnumerable<ICommandHandler> commandHandlers,
        ISerializationFormatter formatter)
    {
        _commandHandlers = commandHandlers;
        _formatter = formatter;
    }

    public void Execute(string command)
    {
        if (command == null)
            throw new ArgumentNullException("command");

        var request = _formatter.Deserialize<CommandRequest>(command);

        var handler = _commandHandlers
            .Single(x => x.CommandType == request.Command.GetType());

        handler.Handle(request.Command);
    }
}

private void ConfigureContainer(Container container)
{
    container.Register(typeof(ICommandHandler<>),new[] { typeof(BusinessLayer).Assembly });

    container.RegisterDecorator(typeof(ICommandHandler<>),
        typeof(WeaklyTypedCommandHandler<>));

    //container.RegisterCollection<ICommandHandler>????
}

Solution

  • You should seriously consider removing the non-generic ICommandHandler interface, because its only reason for existence is to be able to do the dispatching, but there are better -more elegant- ways of doing this. What you are doing only complicates your design -and registration- tremendously.

    Instead, rewrite your CommandService to the following:

    public class CommandService : ICommandService {
        private readonly Container _container; 
        private readonly ISerializationFormatter _formatter;
    
        public CommandService(Container container, ISerializationFormatter formatter) {
            _container = container;
            _formatter = formatter;
        }
    
        public void Execute(string command) {
            if (command == null) throw new ArgumentNullException("command");
    
            var request = _formatter.Deserialize<CommandRequest>(command);
            Type handlerType = typeof(ICommandHandler<>)
                .MakeGenericType(request.Command.GetType());           
            dynamic handler = _container.GetInstance(handlerType);
            handler.Handle((dynamic)(request.Command));
        }
    }
    

    You would typically not want to have classes to have a dependency on the DI library, but this class only contains infrastructure logic, so you can move it inside the Composition Root. Since the Composition Root already takes a really strong dependency on your DI library, it is fine to have classes as part of the Composition Root that take a dependency on the container as well.

    Without the non-generic ICommandHandler interface, your registration will be childs play:

    container.Register(typeof(ICommandHandler<>), new[] { typeof(BusinessLayer).Assembly });
    
    container.Register<ICommandService, CommandService>(Lifestyle.Singleton);
    

    Last note, ditch the in keyword of the ICommandHandler<in TCommand> interface. Comands and their handlers have a one-to-one mapping; you will never have a derived command and its base command that use the same command handler implementation. Using the in keyword only communicates the wrong message to other developers. Yes I know that Resharper tells you to do so, but you shouldn't blindly follow your tools. Resharper knows shit about your design ;-). Ditch the in!

    But if you hate using dynamic and reflection, I would suggest the following model:

    public sealed class CommandService : ICommandService {
        private readonly Container _container; 
        private readonly ISerializationFormatter _formatter;
    
        public CommandService(Container container, ISerializationFormatter formatter) {
            _container = container;
            _formatter = formatter;
        }
    
        public void Execute(string command) {
            if (command == null) throw new ArgumentNullException("command");
    
            var request = _formatter.Deserialize<CommandRequest>(command);
            Type handlerType = typeof(WeaklyTypedCommandHandler<>)
                .MakeGenericType(request.Command.GetType());           
            var handler = (ICommandHandler)_container.GetInstance(handlerType);
            handler.Handle(request.Command);
        }
    
        private interface ICommandHandler {
            void Handle(object command);
        }
    
        public class WeaklyTypedCommandHandler<TCommand> : ICommandHandler {
            private readonly ICommandHandler<TCommand> _commandHandler;
    
            public WeaklyTypedCommandHandler(ICommandHandler<TCommand> commandHandler) {
                _commandHandler = commandHandler;
            }
    
            public void Handle(object command) {
                _commandHandler.Handle((TCommand)command);
            }
        }
    }
    

    This model mixes both worlds, as it:

    • Uses the container to get a WeaklyTypedCommandHandler.
    • This handler implements ICommandHandler (but not ICommandHandler<T>), so you can do two simple casts and don't need reflection.
    • Both the WeaklyTypedCommandHandler and non-generic ICommandHandler are implementation details of the CommandService class.
    • Compared to dynamic, this model will keep working, even if your command handler implementations (or decorators) are (accidentally) defined as internal.
    • Just like dynamic, but compared to using the reflection API (using Type.GetMethod().Invoke()), thrown exceptions from within command handlers will not get wrapped in TargetInvocationExceptions.

    Downside of this model is that you need an extra class and interface, although the amount of extra code is minimal.