结构图 - 同一接口的两个实现

structuremap - two implementations of same interface

我有一个服务 class 与以下 ctor:

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

IMessageService 的两个实现(电子邮件和短信)。如何配置容器以正确解析此构造函数?这是命名实例的用武之地,还是另一种情况?

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

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

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

// 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 个参数的服务中。

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

//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
    }
}

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

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

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

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

希望对您有所帮助