2014-11-14 65 views
2

單消息處理廠因此,我有叫IMessage。然後我有類,比如一個標記接口:使用Autofac

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

如果我理解正確,你有每個消息的工廠?您可以使用IIndex (http://docs.autofac.org/en/latest/advanced/keyed-services.html) – MHGameWork 2014-11-15 13:19:30

+0

此外,這可能有所幫助:http://stackoverflow.com/questions/25003048/keyed-delegate-factories-with-runtime-constructor-parameters – MHGameWork 2014-11-15 13:20:45

+0

是的,我有一個每個消息處理程序的工廠(每個消息類型有一個消息處理程序)。感謝您的鏈接。 – fgauna 2014-11-16 16:41:19

回答

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

謝謝!回覆晚了非常抱歉。我最終將你創建的第二個IMessageHandler接口重命名爲IMessageHandlerAdapter,因爲它對我有點困惑。同樣在'ReceiveMessage'方法中,你可以使用'this.handlers.FirstOrDefault(h => h.MessageType == message.GetType());'而不是 – fgauna 2015-01-13 16:27:00

0

對於任何人誰仍在尋找自動分派給註冊了適當的消息處理程序更好的解決方案,還有通過MediatR。這一個很好的實現是可以分派消息與適當登記處理真棒庫,並有能力發佈消息給多個處理程序。 它最適合CQRS場景,也適用於異步Web API,請參閱CQRS using MediatR。使用DI容器(如Autofac和StructuredMap)時有很好的支持,請參閱MediatR wiki的wiki頁面以獲取有關DI支持的全部詳細信息。