structuremap - 同一接口的两个实现

时间:2015-02-19 09:12:40

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

我有一个服务类,包含以下ctor:

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

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

4 个答案:

答案 0 :(得分:30)

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

// 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);

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

public class CompositeMessageService : IMessageService
{
    private readonly IMessageService messageServices;

    public CompositeMessageService(IMessageService[] messageServices)
    {
        if (messageServices == null)
            throw new ArgumentNullException("messageServices");
        this.messageServices = 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个参数的服务中。

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

答案 1 :(得分:1)

结束命名实例是一种可能的解决方案:

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

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

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

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

希望有所帮助