使用Autofac的单消息处理程序工厂

时间:2014-11-14 21:55:30

标签: c# dependency-injection autofac

所以我有一个名为IMessage的标记界面。然后我有类:

public class MessageA: IMessage 
{
}

然后我将消息处理程序定义为:

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

我想在收到新邮件时将这些消息重新路由到相应的消息处理程序。例如:

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

        messageHandler.Handle(message);
    }
}

我现在可以通过使用下面的工厂来实现这一目标,但我需要依赖于每个不同类型的消息的新工厂。所以我想知道是否有办法创建一个足够智能的单一工厂来解决适当的消息处理程序?

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注册

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

2 个答案:

答案 0 :(得分:3)

首先,我为您的消息做一个小实现,只是一个基本的处理标志,以便测试

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

现在让我们将两个处理程序实现为:

public class MessageAHandler : IMessageHandler<MessageA>
{
    public void Handle(MessageA message)
    {
        message.MarkAsHandled();
    }
}

public class MessageBHandler : IMessageHandler<MessageB>
{
    public void Handle(MessageB message)
    {
        message.MarkAsHandled();
    }
}

作为旁注,您可能希望将IMessageHandler接口标记为公共(如果可见性设置为内部,则会出现编译器错误。)

现在让我们添加一个小处理程序:

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

我们现在可以这样实现MessageReceiver:

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

现在测试我们的消息是否正确处理,这是一个小测试:

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

    }
}

我们需要稍微修改注册,如果选择手动注册,我们按照以下步骤进行:

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

在这里,我们现在需要注册一个处理程序和相关的适配器。

当然也可以执行汇编扫描,但这需要更多的管道,因为使用:

builder.RegisterAssemblyTypes(ThisAssembly)
    .Where(x => x.IsAssignableFrom(typeof(IMessageHandler<IMessage>)))
    .InstancePerDependency().AsImplementedInterfaces();

不起作用

typeof(MessageAHandler).IsAssignableFrom(typeof(IMessageHandler<IMessage>))

将返回false,因为MessageAHandler实现了IMessageHandler,而不是IMessageHandler

要进行自动发现和注册,请参阅以下代码段:

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

答案 1 :(得分:0)

对于仍在寻找更好的自动调度解决方案的人来说,通过MediatR有一个很好的实现。这是一个很棒的库,可以将消息发送给适当的注册处理程序,并且具有将消息发布到多个处理程序。 它最适合CQRS场景,也适用于Async Web API,请参考CQRS using MediatR。使用像Autofac和StructuredMap这样的DI容器时有很好的支持。有关DI支持的详细信息,请参阅MediatR wiki的Wiki页面。