Structmap - две реализации одного и того же интерфейса

У меня есть класс обслуживания со следующим ctor:

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

и две реализации IMessageService (электронная почта и смс). Как настроить контейнер для правильного решения этого конструктора? Является ли это тем, в который входят именованные экземпляры, или это для другого сценария?

Ответ 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);

Но я бы сказал, что ваш дизайн нуждается в некоторой работе. Тот факт, что ваша служба знает разницу между службой электронной почты и услугой SMS, является нарушением Принципа замещения Лискова. Лучший подход, чем введение двух параметров одного и того же типа, заключается в использовании составного шаблона .

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 параметра.

Кроме того, вы получаете возможность добавлять дополнительные службы сообщений или удалять существующие службы сообщений без изменения дизайна.

Ответ 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();
    } 

Надеюсь, что это поможет