Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Single Message Handler Factory using Autofac

So I have a marker interface called IMessage.Then I have classes like:

public class MessageA: IMessage 
{
}

Then I have message handlers defined as:

internal interface IMessageHandler<in T> where T: IMessage
{
    void Handle(T message);
}

public class MessageAHandler : IMessageHandler<MessageA> 
{
    public void Handle(T message)
    { 
        //Some logic here
    }
}

I want to re-route these messages that I get to the corresponding message handlers when I get a new message. Example:

public class MessageReceiver
{
    public void ReceiveMessage(IMessage message)
    {
        //somehow resolve the appropiate message handler here

        messageHandler.Handle(message);
    }
}

I can accomplish this right now by using factories like below but I need to have a dependency on each a new factory per different type of message. So I'm wondering if there is a way to create a single factory that will be smart enough to resolve the appropiate message handler?

public class MessageReceiver
{
   private readonly Func<IMessageHandler<MessageA>> _messageAFactory;

   public MessageReceiver(Func<IMessageHandler<MessageA>> messageAFactory)
   {
       _messageAFactory= messageAFactory;
   }

   public void ReceiveMessage(IMessage message)
   {
       if (message is MessageA)
       {
           var messageHandler = _messageAFactory();
           messageHandler.Handle(message as MessageA);
       }

       // Add more if-statements here for more messages
   }
}

Autofac Registration

public class InfrastructureModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        //Register the types in the infrastructure assembly
        builder.RegisterAssemblyTypes(ThisAssembly).AsImplementedInterfaces()
            .InstancePerLifetimeScope();

        //Register the message handlers
        builder.RegisterAssemblyTypes(ThisAssembly)
        .Where(x => x.IsAssignableFrom(typeof(IMessageHandler<IMessage>)))
        .InstancePerDependency().AsImplementedInterfaces();
    }
}
like image 209
gaunacode.com Avatar asked Feb 06 '26 23:02

gaunacode.com


1 Answers

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();
    }
like image 197
mrvux Avatar answered Feb 08 '26 14:02

mrvux



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!