Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic message handlers

Tags:

c#

generics

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:

  • I want to have a couple of classes for messages i.e. 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.

like image 530
kubal5003 Avatar asked Feb 15 '15 21:02

kubal5003


People also ask

Is there a generic interface for imessagehandler?

// 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);...

What is an example of a message handler?

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:

What is the Order in which messagehandlers are called?

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.

How to write a custom message handler in http?

To write a custom message handler, derive from System.Net.Http.DelegatingHandler and override the SendAsync method. This method has the following signature:


3 Answers

Option 1

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 });
}

Option 2

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");
    }
}
like image 94
Mouhong Lin Avatar answered Oct 11 '22 05:10

Mouhong Lin


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);
        }
    }
like image 22
AK_ Avatar answered Oct 11 '22 06:10

AK_


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);
like image 1
Groo Avatar answered Oct 11 '22 06:10

Groo