Here's a solution I came up with, but it does have some drawbacks. You'll have to decide if the drawbacks are acceptable for your situation. However, the benefit is that configuration is based on convention and adding new IMessage
and IMessageHandler
types won't require any additional Windsor configuration.
I used a combination of Windsor's TypedFactoryFacility
along with a custom ITypedFactoryComponentSelector
. By using a factory, I remove the need for your code to directly call the container's Resolve
method. When a class needs to get an IMessageHandler
based on a message type, it can just have a dependency on the factory. Here's the factory:
public interface IMessageHandlerFactory
{
IMessageHandler GetMessageHandler(string messageType);
}
Since I'm using Windsor's TypedFactoryFacility
I don't need to implement this method, but when it comes to calling the GetMessageHandler
method, I'll "help" Windsor pick the proper component by defining a custom component selector:
public class HandlerTypeSelector : DefaultTypedFactoryComponentSelector
{
private readonly WindsorContainer container;
public HandlerTypeSelector(WindsorContainer container)
{
this.container = container;
}
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
if (method.Name == "GetMessageHandler")
{
var type = arguments[0].ToString();
var messageHandlers = container.ResolveAll<IMessageHandler>();
var single = messageHandlers.SingleOrDefault(h => h.GetMessageType() == type);
if( single != null)
return single.GetType().FullName;
}
return base.GetComponentName(method, arguments);
}
}
Registering all of this is as simple as:
var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component.For<IMessageHandlerFactory>().AsFactory(c => c.SelectedWith(new HandlerTypeSelector(container))),
AllTypes.FromThisAssembly().BasedOn<IMessage>().WithService.AllInterfaces(),
AllTypes.FromThisAssembly().BasedOn<IMessageHandler>().WithService.AllInterfaces()
);
Here's some test code I used to verify:
var sampleMessage = new RegisterUserMessage();
var factory = container.Resolve<IMessageHandlerFactory>();
var handler = factory.GetMessageHandler(sampleMessage.MessageType);
And the classes/interfaces I used to test with:
public interface IMessage
{
string MessageType { get; }
}
public interface IMessageHandler
{
string GetMessageType();
}
public class RegisterUserMessage : IMessage
{
public string MessageType
{
get { return "RegisterUser"; }
}
}
public class RegisterUserMessageHandler : IMessageHandler
{
public string GetMessageType()
{
return "RegisterUser";
}
}
public class RemoveUserMessage : IMessage
{
public string MessageType
{
get { return "RemoveUser"; }
}
}
public class RemoveUserMessageHandler : IMessageHandler
{
public string GetMessageType()
{
return "RemoveUser";
}
}
Now, the drawback: Inside the component selector, I resolve all IMessageHandler
's and then decide which one to use based on the name (since I didn't name the message handlers when registering them, they'll be registered in Windsor with their fully qualified name). If your message handlers are lightweight and/or there's not many of them, or they're static, this might not be an issue. If they are transient and/or have significant initialization code, resolving all of them every time you need a single one could be costly. In that case, I'd scrap this whole approach and go with something that is more configuration-heavy, but allows resolving a single message handler directly by name.