First I'll make a small implementation for your messages, just a basic handled flag, in order to test
public class MessageA : IMessage
{
public bool Handled
{
get;
private set;
}
public void MarkAsHandled()
{
this.Handled = true;
}
}
public class MessageB : IMessage
{
public bool Handled
{
get;
private set;
}
public void MarkAsHandled()
{
this.Handled = true;
}
}
Now let's implement both handlers as:
public class MessageAHandler : IMessageHandler<MessageA>
{
public void Handle(MessageA message)
{
message.MarkAsHandled();
}
}
public class MessageBHandler : IMessageHandler<MessageB>
{
public void Handle(MessageB message)
{
message.MarkAsHandled();
}
}
As a side note, you might want to mark your IMessageHandler interface as public (I get compiler error if visibility is set as internal).
Now let's add a small handler:
public interface IMessageHandler
{
Type MessageType { get; }
void Handle(IMessage message);
}
public class MessageHandlerAdapter<T> : IMessageHandler where T : IMessage
{
private readonly Func<IMessageHandler<T>> handlerFactory;
public MessageHandlerAdapter(Func<IMessageHandler<T>> handlerFactory)
{
this.handlerFactory = handlerFactory;
}
public void Handle(IMessage message)
{
var handler = handlerFactory();
handler.Handle((T)message);
}
public Type MessageType
{
get { return typeof(T); }
}
}
We can now implement MessageReceiver this way:
public class MessageReceiver
{
private readonly IEnumerable<IMessageHandler> handlers;
public MessageReceiver(IEnumerable<IMessageHandler> handlers)
{
this.handlers = handlers;
}
public void ReceiveMessage(IMessage message)
{
var handler = this.handlers.Where(h => h.MessageType == message.GetType()).FirstOrDefault();
if (handler != null)
{
handler.Handle(message);
}
else
{
//Do something here, no handler found for message type
}
}
}
Now to test that our messages are processed properly, here is a small test:
[TestClass]
public class TestSelector
{
private IContainer container;
[TestMethod]
public void TestMethod()
{
var processor = container.Resolve<MessageReceiver>();
MessageA ma = new MessageA();
MessageB mb = new MessageB();
processor.ReceiveMessage(ma);
processor.ReceiveMessage(mb);
Assert.AreEqual(ma.Handled, true);
Assert.AreEqual(mb.Handled, true);
}
}
And we need to modify registration a bit, if opting for manual registration, we do as follow:
public TestSelector()
{
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<MessageAHandler>().As<IMessageHandler<MessageA>>();
containerBuilder.RegisterType<MessageBHandler>().As<IMessageHandler<MessageB>>();
containerBuilder.RegisterType<MessageHandlerAdapter<MessageA>>().As<IMessageHandler>();
containerBuilder.RegisterType<MessageHandlerAdapter<MessageB>>().As<IMessageHandler>();
containerBuilder.RegisterType<MessageReceiver>();
this.container = containerBuilder.Build();
}
In here, we now need to register one handler and the relevant adapter.
It is also of course possible to perform assembly scan, but this requires a little bit more plumbing, since using:
builder.RegisterAssemblyTypes(ThisAssembly)
.Where(x => x.IsAssignableFrom(typeof(IMessageHandler<IMessage>)))
.InstancePerDependency().AsImplementedInterfaces();
will not work
typeof(MessageAHandler).IsAssignableFrom(typeof(IMessageHandler<IMessage>))
will return false, since MessageAHandler implements IMessageHandler, not IMessageHandler
To do automatic discovery and registration, here is a snippet:
public TestSelector()
{
var containerBuilder = new ContainerBuilder();
Func<Type, Type> GetHandlerInterface = (t) => t.GetInterfaces()
.Where(iface => iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IMessageHandler<>)).FirstOrDefault();
var handlerTypes = typeof(IMessage).Assembly.GetTypes()
.Where(type => type.IsClass
&& !type.IsAbstract
&& GetHandlerInterface(type) != null);
foreach (Type handlerType in handlerTypes)
{
Type messageType = GetHandlerInterface(handlerType).GetGenericArguments()[0];
var genericHandler = typeof(MessageHandlerAdapter<>).MakeGenericType(messageType);
containerBuilder.RegisterType(handlerType).AsImplementedInterfaces();
containerBuilder.RegisterType(genericHandler).As<IMessageHandler>();
}
containerBuilder.RegisterType<MessageReceiver>();
this.container = containerBuilder.Build();
}