Замок Виндзор: введите IEnumerable <IService>, используя только подмножество зарегистрированных компонентов для IService - PullRequest
0 голосов
/ 08 октября 2018

Рассмотрим следующий сценарий служб и компонентов в образце консольного приложения C #

public interface IService { }
public class FooService: IService { }
public class BarService: IService { }
public class BuzzService: IService { }
public class AwesomeService: IService { }

public class Consumer 
{
    public Consumer(IEnumerable<IService> services)
    {
        // do some initilization work here...
    }
}

public class AnotherConsumer 
{
    public AnotherConsumer(IEnumerable<IService> services)
    {
        // do some initilization work here...
    }
}

Представим себе, что нужно выполнить следующие регистрации внутри корня композиции:

var container = new WindsorContainer();

container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel, true));

container.Register(Component.For<IService>().ImplementedBy<FooService>());
container.Register(Component.For<IService>().ImplementedBy<BarService>());
container.Register(Component.For<IService>().ImplementedBy<BuzzService>());
container.Register(Component.For<IService>().ImplementedBy<AwesomeService>());

container.Register(Component.For<Consumer>());
container.Register(Component.For<AnotherConsumer>());

// consumer got injected all 4 different implementations of IService
// due to CollectionResolver
var consumer = container.Resolve<Consumer>();

// anotherConsumer got injected all 4 different implementations of 
// IService due to CollectionResolver
var anotherConsumer = container.Resolve<AnotherConsumer>(); 

Этот видСценарий работает нормально, и я делал это несколько раз.

Что, если по какой-то причине я хотел бы внедрить внутри конструктора класса Consumer * только 1010 * двух разных реализаций IService , например, только FooService и BarService (, продолжая внедрять все доступные реализации IService в конструктор AnotherConsumer )?

Есть ли элегантный способ сделать это?

Ответы [ 2 ]

0 голосов
/ 09 октября 2018

Ответ, который дал @Steven, очень актуален.Для этого есть немного более приятный API (добавленный в версии 3 Windsor, поэтому он не рассматривается в вышеупомянутой книге) без необходимости использования .Named или регистрации всех ваших IService по одному.

container.Register(Component
    .For<Consumer>()
    .DependsOn(
       Dependency.OnComponentCollection(
          "services", typeof(FooService), typeof(BarService)
       )
    )
);

Или, если вы не хотите использовать имя зависимости, вы можете указать его по типу

container.Register(Component
    .For<Consumer>()
    .DependsOn(
       Dependency.OnComponentCollection<IEnumerable<IService>>(
          typeof(FooService), typeof(BarService)
       )
    )
);

. Таким образом, вы можете просто зарегистрировать свои IComponent с помощью соглашения, подобного следующему, без каких-либо дополнительныхсуета.

container.Register(Classes.FromThisAssembly()
    .BasedOn<IService>()
    .WithServiceBase()
    .LifestyleTransient());
0 голосов
/ 09 октября 2018

Я взял свою копию первого издания из Внедрение зависимостей в .NET .Он содержит полную главу о замке Виндзор и обсуждает этот точный сценарий.Хитрость заключается в том, чтобы сделать две вещи:

  • Определить регистрации коллекций как именованные регистрации, используя .Named(string)
  • Указать переопределение для Consumer, используя .ServiceOverrides(object)

Следующий пример кода почти прямо из книги (имена заменены на ваши примеры):

container.Register(Component
    .For<IService>()
    .ImplementedBy<FooService>()
    .Named("Foo"));
container.Register(Component
    .For<IService>()
    .ImplementedBy<BarService>()
    .Named("Bar"));
container.Register(Component
    .For<IService>()
    .ImplementedBy<BuzzService>()
    .Named("Buzz"));
container.Register(Component
    .For<IService>()
    .ImplementedBy<AwesomeService>()
    .Named("Awesome"));

container.Register(Component
    .For<Consumer>()
    .ServiceOverrides(new
        {
            services = new[] { "Foo", "Bar" }
        }));

container.Register(Component.For<AnotherConsumer>());

var consumer = container.Resolve<Consumer>();
...