【问题标题】:Single Message Handler Factory using Autofac使用 Autofac 的单个消息处理程序工厂
【发布时间】:2015-01-12 09:42:36
【问题描述】:

所以我有一个名为 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();
    }
}

【问题讨论】:

标签: c# dependency-injection autofac


【解决方案1】:

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

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

要做自动发现和注册,这里有一个sn-p:

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

【讨论】:

  • 谢谢!回复晚了非常抱歉。我最终将您创建的第二个IMessageHandler 接口重命名为IMessageHandlerAdapter,因为这让我有点困惑。同样在ReceiveMessage 方法中,您可以改用this.handlers.FirstOrDefault(h =&gt; h.MessageType == message.GetType());
【解决方案2】:

对于仍在寻找更好的解决方案来自动发送到已注册的适当消息处理程序的任何人,通过MediatR 有一个很好的实现。这是一个很棒的库,可以将消息发送到适当的注册处理程序,并且能够发布消息到多个处理程序。 它最适合 CQRS 场景以及异步 Web API,请参阅 CQRS using MediatR。使用 DI 容器(如 Autofac 和 StructuredMap)时有很好的支持,有关 DI 支持的完整详细信息,请参阅 MediatR wiki 的 wiki 页面。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    • 2013-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多