I want to create a generic mechanism for handling messages in C#. I have a need for this in my small application, so I don't want to use full blown message bus. My requirements are quite simple:
Message1
, Message2
. They can inherit from one base class, that's not a problem, but if they don't I don't care. Currently they do inherit from Message
.be able to get handler for each and every message class. i.e. if I send Message1
, then Message1Handler
class should be instantiated. Handlers have to implement IMessageHandler<T>
where T
is the message class. IMessageHandler
is defined as follows:
interface IMessageHandler<T>
{
void Execute(T message);
}
I wrote a simple "Resolver" class:
public static class HandlerRegistry
{
private static readonly Dictionary<string, Type> _handlers = new Dictionary<string, Type>();
public static void Register<T, T2>() where T2: IMessageHandler<T>
{
_handlers.Add(typeof(T).FullName, typeof(T2));
}
public static IMessageHandler<T> Resolve<T>(T parameters)
{
var type = _handlers[parameters.GetType().FullName];
return (IMessageHandler<T>) Activator.CreateInstance(type);
}
}
In this implementation everything is OK, but one part - the cast to IMessageHandler. When I'm trying to use this with a collection of messages this is what happens: the compiler doesn't know at compile time what actual messages are going to be in the collection - it just assumes that they are all subclasses of Message
, so it's trying to cast IMessageHandler<ConcreteMessage>
to IMessageHandler<Message>
and obviously I'm getting an exception with invalid cast. In this case probably contravariance would help, but I'm not able to declare the parameter as out because I have the message in the Execute method parameters.
Does anyone know an elegant solution to this problem? I know I can make it "more runtime" - instead of using generics just declare
void Execute(Message m)
and in each and every handler start with trying to cast to the type that I'm expecting, but as someone said somewhere - each and every cast that you write undermines the whole point of using a type system.
// NOTE: This interface is not generic public interface IMessageHandler { void Execute (object message); } public abstract class MessageHandler: IMessageHandler { public abstract void Execute (T message); // NOTE: Here we explicitly implement the IMessageHandler void IMessageHandler.Execute (object message) { Execute ((T)message);...
For example, a message handler might: Read or modify request headers. Add a response header to responses. Validate requests before they reach the controller. This diagram shows two custom handlers inserted into the pipeline:
Message handlers are called in the same order that they appear in MessageHandlers collection. Because they are nested, the response message travels in the other direction. That is, the last handler is the first to get the response message.
To write a custom message handler, derive from System.Net.Http.DelegatingHandler and override the SendAsync method. This method has the following signature:
If you do not care using reflection. You can add a Execute
method to your HandlerRegistry
instead of returning the handler back to the caller:
public static void Execute<T>(T parameters)
{
var type = _handlers[parameters.GetType().FullName];
var handler = Activator.CreateInstance(type);
type.GetMethod("Execute", new[] { parameters.GetType() })
.Invoke(handler, new object[] { parameters });
}
If you do not care that one message handler can only subscribe to one message. We can take advantage of the Explicit Interface Implementation feature of C#:
// NOTE: This interface is not generic
public interface IMessageHandler
{
void Execute(object message);
}
public abstract class MessageHandler<T> : IMessageHandler
{
public abstract void Execute(T message);
// NOTE: Here we explicitly implement the IMessageHandler
void IMessageHandler.Execute(object message)
{
Execute((T)message);
}
}
Now your resolve method can change to:
public static IMessageHandler Resolve<T>(T parameters)
{
var type = _handlers[parameters.GetType().FullName];
return (IMessageHandler)Activator.CreateInstance(type);
}
By the way, personally I would prefer to pass in a Type
instead of the message instance.
Then make your handlers inherit from the generic abstract MessageHandler<T>
instead of implementing IMessageHandler
:
public class HandlerA : MessageHandler<MessageA>
{
public override void Execute(MessageA message)
{
Console.WriteLine("Message A");
}
}
public class HandlerB : MessageHandler<MessageB>
{
public override void Execute(MessageB message)
{
Console.WriteLine("Message B");
}
}
How about this for a message router:
class Tester
{
public void Go()
{
var a = new MessageA();
var b = new MessageB();
var c = new MessageC();
var router = new MessageRouter();
router.RegisterHandler(new HandlerA());
router.RegisterHandler(new HandlerB());
router.Route(a);
router.Route(b);
router.Route(c);
}
}
class MessageRouter
{
Dictionary<Type, dynamic> m_handlers = new Dictionary<Type,dynamic>();
public void RegisterHandler<T>(IMessageHandler<T> handler)
{
m_handlers.Add(typeof(T), handler);
}
public void Route(dynamic message)
{
var messageType = message.GetType();
if (m_handlers.ContainsKey(messageType))
{
m_handlers[messageType].Handle(message);
}
else
{
foreach (var pair in m_handlers)
{
if(pair.Key.IsAssignableFrom(messageType))
{
pair.Value.Handle(message);
}
}
}
}
}
class MessageA
{
public virtual string A { get { return "A"; } }
}
class MessageB
{
public string B { get { return "B"; } }
}
class MessageC :MessageA
{
public override string A { get { return "C"; } }
}
interface IMessageHandler<T>
{
void Handle(T message);
}
class HandlerA : IMessageHandler<MessageA>
{
public void Handle(MessageA message)
{
Console.WriteLine(message.A);
}
}
class HandlerB : IMessageHandler<MessageB>
{
public void Handle(MessageB message)
{
Console.WriteLine(message.B);
}
}
How about taking a slightly different approach: Instead of registering the type of a handler, why not register the actual handler instance which will process the message? This gives you much greater flexibility in instantiation of the handler, and removes any type ambiguities.
The idea is to be able to do this:
// have several handler classes
class FooMessageHandler : IMessageHandler<Foo>
{ }
class BarMessageHandler : IMessageHandler<Bar>
{ }
// have them instantiated - allows you to pass much more context
// than Activator.CreateInstance is able to do
var fooMessageHandler = new FooMessageHandler(various params);
var barMessageHandler = new BarMessageHandler(various params);
// register actual instances
HandlerRegistry.Register<Foo>(fooMessageHandler);
HandlerRegistry.Register<Bar>(barMessageHandler);
// handler registry will simply dispatch the message to
// one of the handlers
HandlerRegistry.Dispatch(someFooMessage);
Not only that, but the approach allows you to register multiple handlers for each message type:
// these will all get called when a Foo message is received
HandlerRegistry.Register<Foo>(fooMessageHandler);
HandlerRegistry.Register<Foo>(someOtherFooHandler);
HandlerRegistry.Register<Foo>(yetAnotherFooHandler);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With