2015-09-08 2 views
2

У меня есть следующий установщик, но по какой-то нечетной причине он не разрешает правильно. У меня есть интерфейс, в котором есть 2 реализации, но вы хотите ввести правильный экземпляр на основе naming conventions.Замок Виндзор несколько реализаций интерфейса

Я ожидаю в этом случае, что правильный экземпляр ICommand будет введен в соответствии с их именем. Однако по какой-то нечетной причине оба контроллера выбирают самый первый экземпляр, то есть FooCommand из-за его определения сначала в установщике.

Не уверен, что я сделал неправильно? Возможно, есть ли альтернативный способ сделать это?

public interface ICommand { } 

public class FooCommand : ICommand { } 

public class BarCommand : ICommand { } 

public class SomeController : ApiController 
{ 
    public SomeController(ICommand fooCommand) { } 
} 

public class HelloController : ApiController 
{ 
    public HelloController(ICommand barCommand) { } 
} 

container.Register(
    Component.For<ICommand>() 
     .Named("fooCommand") 
     .ImplementedBy<FooCommand>() 
     .LifestyleSingleton(), 
    Component.For<ICommand>() 
     .Named("barCommand") 
     .ImplementedBy<BarCommand>() 
     .LifestyleSingleton()); 
+0

Так что вы хотите, чтобы ввести '' FooCommand' в SomeController' и '' BarCommand' в HelloController'? Но что происходит с этими контроллерами, если вы меняете зависимости, т. Е. Вводите 'BarCommand' в' SomeController'? Разве это прерывает 'SomeController' или он будет продолжать функционировать правильно? – Steven

+0

Это сломает его, так как они оба будут разговаривать с разными базовыми таблицами. –

+0

В этом случае вы нарушаете [Принцип замены Лискова] (https://en.wikipedia.org/wiki/Liskov_substitution_principle), и это корень вашей проблемы. Чтобы решить эту проблему, дайте обеим командам собственный уникальный интерфейс. – Steven

ответ

2

Как @steven сказал, что это не очень хорошая идея, и если не удалось должным образом, может привести к проблемам понятности вниз линии, но если вы знаете, что вы делаете, вы можете построить IContributeComponentModelConstruction, который будет соответствовать конструктор параметры типа ICommand на ваших контроллерах с компонентами Windsor с тем же именем.

public class ControllerCommandMatcher : IContributeComponentModelConstruction 
{ 
    public void ProcessModel(IKernel kernel, ComponentModel model) 
    { 
     // or whatever other condition to bail out quickly 
     if (model.Implementation.Name.EndsWith("Controller") == false) return; 

     foreach (var constructor in model.Constructors) 
     { 
      foreach (var dependency in constructor.Dependencies) 
      { 
       if (dependency.TargetItemType != typeof (ICommand)) continue; 
       dependency.Parameter = new ParameterModel(dependency.DependencyKey, 
        ReferenceExpressionUtil.BuildReference(dependency.DependencyKey)); 
      } 
     } 
    } 
} 

хитрый бит это:

new ParameterModel(dependency.DependencyKey, 
     ReferenceExpressionUtil.BuildReference(dependency.DependencyKey)) 

Это в основном говорит Виндзор, что зависимость (параметр конструктора), например fooCommand должны быть удовлетворены с компонентом одного и того же имени (fooCommand).

Добавьте вкладчиком в контейнер

container.Kernel.ComponentModelBuilder.AddContributor(new ControllerCommandMatcher()); 

Here's the documentation

+0

Я дам этому вихрь и спасибо за сообщение. Хотя я не понимаю последние 2 строки кода, что они делают? Как он узнает, какой экземпляр для инъекций? –

+0

Я обновил проблему с некоторым дополнительным объяснением –

+0

Блестяще спасибо. –