【问题标题】:structuremap - two implementations of same interface结构映射 - 相同接口的两个实现
【发布时间】:2015-02-19 09:12:40
【问题描述】:

我有一个具有以下 ctor 的服务类:

public class (IMessageService emailService, IMessageService smsService)
{ ... }

以及IMessageService 的两个实现(电子邮件和短信)。如何配置容器以正确解析此构造函数?这是命名实例出现的地方,还是其他场景?

【问题讨论】:

    标签: c# dependency-injection inversion-of-control structuremap


    【解决方案1】:

    可以使用命名实例或智能实例来解决这个问题...

    // Named instances
    this.For<IMessageService>().Use<EmailService>().Named("emailService");
    this.For<IMessageService>().Use<SmsService>().Named("smsService");
    
    
    
    // Smart instances
    var emailService = this.For<IMessageService>().Use<EmailService>();
    var smsService = For<IMessageService>().Use<SmsService>();
    
    this.For<ISomeService>().Use<SomeService>()
        .Ctor<IMessageService>("emailService").Is(emailService)
        .Ctor<IMessageService>("smsService").Is(smsService);
    

    但我认为你的设计需要一些工作。您的服务知道 emailService 和 SMS 服务之间的区别这一事实违反了 Liskov 替换原则。比注入 2 个相同类型的参数更好的方法是使用 composite pattern

    public class CompositeMessageService : IMessageService
    {
        private readonly IMessageService[] messageServices;
        
        public CompositeMessageService(IMessageService[] messageServices)
        {
            this.messageServices = messageServices ?? throw new ArgumentNullException(nameof(messageServices));
        }
        
        public void Send(IMessage message)
        {
            foreach (var messageService in this.messageServices)
            {
                messageService.Send(message);
            }
        }
    }
    

    您的原始服务只需要接受IMessageService 的单个实例。它不需要知道它正在处理什么类型的IMessageService 的详细信息。

    public SomeService(IMessageService messageService)
    

    在 StructureMap 中,您可以轻松注册所有 IMessageService 实例,它会自动将它们注入到 IMessageService 的构造函数参数数组中。

    this.Scan(scan =>
            {
                scan.TheCallingAssembly();
                scan.AssemblyContainingType<IMessageService>();
                scan.AddAllTypesOf<IMessageService>();
            });
    

    或者您可以显式注入实例。

            this.For<IMessageService>().Use<CompositeMessageService>()
                .EnumerableOf<IMessageService>().Contains(x =>
                {
                    x.Type<EmailService>();
                    x.Type<SmsService>();
                });
    

    这意味着可以更改您的配置以更改首先调用服务的顺序。在您当前的设计中,这些细节被硬编码到接受 2 个参数的服务中。

    此外,您还可以在不更改设计的情况下添加其他消息服务或删除现有消息服务。

    【讨论】:

    • 绝妙的答案,令人大开眼界。对于如何发现可以像这样使用复合模式的实例,您有什么建议吗?
    • 简短回答 - 具有多个相同类型的依赖项的服务是一种代码异味,可以使用组合或装饰器。至于进入正确的心态,我个人是通过阅读Dependency Injection in .NET 和一些尝试和错误到达那里的。设计模式可以解决很多问题,而单靠 DI 无法解决,这本书非常适合教授何时以及如何应用这些模式。
    • 使用您的第一个示例,您如何避免复合采用所有实现的数组......其中包括复合......采用数组......等等?
    【解决方案2】:

    原来命名实例是一种可能的解决方案:

     _.For<IMessageService >().Use<EmailService>().Named("emailService");
     _.For<IMessageService >().Use<EmailService>().Named("smsService");
    

    【讨论】:

      【解决方案3】:
      //First just write below lines in IOC    
      
      this.For<IMessageService>().Use<EmailService>();
      this.For<IMessageService >().Use<EmailService>();
      
      
      //Then We can directly use it in our constructor injection of our class                             
      //Where we need it
      IEnumerable<IMessageService> messageServices;  
      
      public ClassNeedsInjection(IEnumerable<IMessageService> messageServices)                         
      {
          this.messageServices=messageServices;
          foreach (var messageService in this.messageServices)
          {
              //use both the objects as you wish
          }
      }
      

      【讨论】:

      【解决方案4】:

      不需要太复杂。只需简单地注册一个接口的所有实现即可。

       this.For<IMessageService >().Use<EmailService>(); 
       this.For<IMessageService >().Use<SmsService>();
      

      然后结构映射将自动将 IMessageService 接口的所有实现注入到任何具有接口的 IEnumerable/list/array 的构造函数中。如下:

      private IEnumerator<IMessageService> _current; 
      public SomeClass(IEnumerable<IMessageService> features) {
          _current = features.GetEnumerator();
          } 
      

      希望对你有帮助

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-11-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-13
        相关资源
        最近更新 更多