Autofac - 如何实现使用运行时信息的工厂?

时间:2016-12-29 22:47:05

标签: .net dependency-injection delegates autofac

我有一个工厂,当收到的消息到达时,它会创建消息处理程序。消息处理程序采用多种依赖关系 - 基于传入消息和其他基础结构组件选择的配置对象。

我使用Autofac和委托,我无法避免将MessageHandlerFactory耦合到特定的实现类。

这是我能够将代表注入工厂的唯一方法:

// Handler defines a delegate that Autofac turns into a factory method
public class FtpMessageHandler : IFtpMessageHandler {
    public delegate FtpMessageHandler Factory (Configuration configuration);

    public FtpMessageHandler(Configuration configuration,
        S3Facade s3Facade,
        .... other dependencies.....
    )
    ....
}

// In Autofac module..

    // This is how I'd like to register the component, but it does NOT 
    // work - Autofac fails to resolve the delegate
    builder.RegisterType<FtpMessageHandler>().As<IFtpMessageHandler>();

    // This is what DOES work    
    builder.RegisterType<FtpMessageHandler>();



public class MessageHandlerFactory {
    public FtpMessageHandler.Factory FtpMessageHandlerFactory { get; }
    ...
    public MessageHandlerFactory( FtpMessageHandler.Factory ftpMessageHandlerFactory, ....)

        FtpMessageHandlerFactory = ftpMessageHandlerFactory;
    )

    public IMessageHandler GetMessageHandler(MessageTypeEnum type, Configuration config) {
        if (type == MessageTypeEnum.FTP)
            return FtpMessageHandlerFactory.Invoke(config);
       ....
   )
}

所以......这是一种时尚之后的作品。

但我不满意必须在MessageHandlerFactory中注入具体类型。例如,这排除了在IMessageHandler上使用装饰器而不修改工厂。

有更好的方法吗?

1 个答案:

答案 0 :(得分:0)

我找到了一个解决方案,基于Alex Meyer-Gleaves所描述的方法http://alexmg.com/selectively-resolving-services-at-runtime-with-autofac/

// New class
public class MessageHandlerMetadata
{
    public FileTransportTypeEnum TransportType { get; set; }
}


// Registration    
        builder.Register(c => new FtpMessageHandler( c.Resolve<IS3Facade>(), c.Resolve<IFtpHelper>()))
            .As<IMessageHandler>()
            .WithMetadata<MessageHandlerMetadata>(m => m.For(am => am.TransportType, FileTransportTypeEnum.FTP));

        ....

        builder.Register(
            c => new MessageHandlerFactory(c.Resolve<IConfigurationProvider>(),
                c.Resolve<IEnumerable<Lazy<IMessageHandler, MessageHandlerMetadata>>>()))
            .As<IMessageHandlerFactory>();


// In the Factory

    public MessageHandlerFactory(
        IEnumerable<Lazy<IMessageHandler, MessageHandlerMetadata>> messageHandlers) { ... }


    private IMessageHandler GetHandlerByConfigurationType(FileTransportTypeEnum fileTransportType,
        DestinationConfiguration configuration)
    {

        var lazy = MessageHandlers.FirstOrDefault(h => h.Metadata.TransportType == fileTransportType);
        if (lazy == null)
        {
            throw new ArgumentException($"No handler is registered with File Transport type {fileTransportType}.");
        }
        var handler = lazy.Value;
        handler.Configure(configuration);
        return handler;