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 ICommandHandler
s. 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>????
}
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:
WeaklyTypedCommandHandler
.ICommandHandler
(but not ICommandHandler<T>
), so you can do two simple casts and don't need reflection.WeaklyTypedCommandHandler
and non-generic ICommandHandler
are implementation details of the CommandService
class.dynamic
, this model will keep working, even if your command handler implementations (or decorators) are (accidentally) defined as internal.dynamic
, but compared to using the reflection API (using Type.GetMethod().Invoke()
), thrown exceptions from within command handlers will not get wrapped in TargetInvocationException
s.Downside of this model is that you need an extra class and interface, although the amount of extra code is minimal.