How to get Castle Windsor command/handler based on Message content (not runtime type)

641 Views Asked by At

I have (non-polymorphic) messages, which I want to handle according to their MessageType field. It is close to the command-pattern discussed here (and which I have working with tests):

  1. Castle Windsor & Command Pattern
  2. Windsor registration for generic commands/command handlers
  3. http://kozmic.net/2010/03/11/advanced-castle-windsor-ndash-generic-typed-factories-auto-release-and-more/

Execpt, I don't have separate classes or generic types for each MessageType. I would prefer not to have to introduce such types (legacy reasons).

Roughly like:

public interface IMessage {
   int MessageType { get; }
   IList<byte> Payload { get; }
}
class Message: IMessage {
   // Implementation
}
public interface IHandler {
   void Handle(IMessage message);
}
public abstract class TypedMessageHandler: IHandler {
   public abstract int RequiredMessageType { get; }
   public virtual void Handle(IMessage message) {
     if (message.CommandType != RequiredMessageType)
       throw new ArgumentException(nameof(message)) // More info :)
     InnerHandle(message);
   }
   protected abstact void InnerHandle(IMessage message);
}
class MessageType0Handler: TypedMessageHandler {
   public int RequiredMessageType => 0;
   override void InnerHandle(IMessage message) => // Do stuff;
}
class MessageType1Handler: TypedMessageHandler {
   public int RequiredMessageType => 1;
   override void InnerHandle(IMessage message) => // Do stuff;
}
// Some registration that I can use to dispatch from `msg: IMessage` to 
// `IHandler` with `RequiredMessageType == msg.MessageType`.

How can I do this dispatch with Windsor? Preferably with some registrations based on queries on the defined types.

The trick with a TypedFactory from the above articles is not immediately useful.

I've been playing with tagging the handlers with custom attributes, but I can't figure out how to both:

  1. Get the command argument (IMessage instance), and
  2. use a custom IHandlerSelector to filter the IHandler implementations based on such an attribute.
2

There are 2 best solutions below

2
Jan Muncinsky On

I would recommend to add RequiredMessageType to your IHandler interface:

public interface IHandler
{
    void Handle(IMessage message);
    int RequiredMessageType { get; }
}

Than you can create your own Dispatcher:

public class Dispatcher
{
    private readonly Dictionary<int, IHandler> handlers;

    public Dispatcher(IEnumerable<IHandler> handlers)
    {
        this.handlers = handlers.ToDictionary(h => h.RequiredMessageType, h => h);
    }

    public void Dispatch(IMessage message)
    {
        handlers[message.MessageType].Handle(message);
    }
}

And Castle registration would be something like:

var container = new WindsorContainer();

container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
container.Register(Component.For<Dispatcher>());
container.Register(
Classes.FromThisAssembly()
.BasedOn(typeof(IHandler))
.WithServiceFromInterface(typeof(IHandler)));

//dispatch
var dispatcher = container.Resolve<Dispatcher>();
dispatcher.Dispatch(new Message());
1
Krzysztof Kozmic On

Probably the simplest way to do it would be to register your handlers as named components following a well-known pattern (perhaps "message-handler-for-N" where N is the MessageType and then have a typed factory with a custom ITypedFactoryComponentSelector that given a message picks the name for the handler to resolve.