Autofac 根据父依赖选择嵌套实现

Autofac pick nested implementation based on parent dependency

鉴于下面的示例依赖关系树,我想 select 底层 Config 实例基于从容器中解析的顶级依赖关系,例如TopLevelMessageConsumer 将解析与 TopLevelMessageDispatcher 相同的 IMessageServiceIMessageQueue 实现,但每个实现都有自己的 Config 实例。

- TopLevelMessageConsumer
 - IMessageService
  - IMessageQueue
   - Config
- TopLevelMessageDispatcher
 - IMessageService
  - IMessageQueue
   - Config

我知道使用 KeyedNamed 是可能的,但这需要树中的每个依赖项根据配置变化的数量进行 n 次注册。结果:

containerBuilder.RegisterInstance(config1).Keyed<Config>(Key.One).SingleInstance();
containerBuilder.RegisterInstance(config2).Keyed<Config>(Key.Two).SingleInstance();

containerBuilder.RegisterType<MessageQueue>().Keyed<IMessageQueue>(Key.One).SingleInstance()
    .WithParameter(ResolvedParameter.ForKeyed<Config>(Key.One));
containerBuilder.RegisterType<MessageQueue>().Keyed<IMessageQueue>(Key.Two).SingleInstance()
    .WithParameter(ResolvedParameter.ForKeyed<Config>(Key.Two));

containerBuilder.RegisterType<MessageService>().Keyed<IMessageService>(Key.One).SingleInstance()
    .WithParameter(ResolvedParameter.ForKeyed<IMessageQueue>(Key.One));
containerBuilder.RegisterType<MessageService>().Keyed<IMessageService>(Key.Two).SingleInstance()
    .WithParameter(ResolvedParameter.ForKeyed<IMessageQueue>(Key.Two));

containerBuilder.RegisterType<TopLevelMessageConsumer>().AsSelf().SingleInstance()
    .WithParameter(ResolvedParameter.ForKeyed<IMessageService>(Key.One));
containerBuilder.RegisterType<TopLevelMessageDispatcher>().AsSelf().SingleInstance()
    .WithParameter(ResolvedParameter.ForKeyed<IMessageService>(Key.Two));

是否有 better/cleaner 注册方式?

您可以通过使用通用接口而不是显式 DI 注册来大大简化这一过程。唯一的条件是您需要使用某种类型来区分服务。在这种情况下,进行单独配置 类 是很自然的选择。

配置

public class ConsumerConfig : IConfig { }
public class DispatcherConfig : IConfig { }

接口

// Define interface of config here (you may opt for abstract class instead)
public interface IConfig { } 
public interface IMessageService<TConfig> { }
public interface IMessageQueue<TConfig> { }

服务

public class MessageService<TConfig> : IMessageService<TConfig> where TConfig : IConfig
{
    public MessageService(IMessageQueue<TConfig> messageQueue)
    {
    }
}

public class MessageQueue<TConfig> : IMessageQueue<TConfig> where TConfig : IConfig
{
    public MessageQueue(TConfig config)
    {
    }
}

public class TopLevelMessageDispatcher
{
    public TopLevelMessageDispatcher(IMessageService<DispatcherConfig> messageService)
    {
    }
}

public class TopLevelMessageConsumer
{
    public TopLevelMessageConsumer(IMessageService<ConsumerConfig> messageService)
    {
    }
}

用法

class Program
{
    static void Main(string[] args)
    {
        // Begin composition root
        var containerBuilder = new ContainerBuilder();

        var config1 = new ConsumerConfig();
        var config2 = new DispatcherConfig();

        containerBuilder.RegisterInstance(config1).AsSelf().SingleInstance();
        containerBuilder.RegisterInstance(config2).AsSelf().SingleInstance();

        containerBuilder.RegisterGeneric(typeof(MessageQueue<>))
            .As(typeof(IMessageQueue<>)).SingleInstance();
        containerBuilder.RegisterGeneric(typeof(MessageService<>))
            .As(typeof(IMessageService<>)).SingleInstance();
        containerBuilder.RegisterType<TopLevelMessageConsumer>()
            .AsSelf().SingleInstance();
        containerBuilder.RegisterType<TopLevelMessageDispatcher>()
            .AsSelf().SingleInstance();

        var container = containerBuilder.Build();
        // End composition root

        var dispatcher = container.Resolve<TopLevelMessageDispatcher>();
        var consumer = container.Resolve<TopLevelMessageConsumer>();
    }
}

请注意,服务的实现不一定需要担心类型是通用的这一事实 - 唯一需要更改的是构造函数签名以更明确地调用通用类型。