温莎城堡:<IService>仅使用IService的注册组件子集注入IEnumerable



请考虑示例 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 类的构造函数中只注入两个不同的 IService 实现,例如只有 FooService 和 BarService(同时仍然继续在 OtherConsumer 的构造函数中注入 IService 的所有可用实现(怎么办?

有没有一种优雅的方法可以做到这一点?

我拿起了第一版的《依赖注入原则、实践和模式》。它包含关于温莎城堡的完整章节,并讨论了这个确切的情况。诀窍是做两件事:

  • 使用.Named(string)将集合注册定义为命名注册
  • 使用.ServiceOverrides(object)指定Consumer的覆盖

下面的代码示例几乎是直接出自本书的(名称替换为您的示例(:

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

@Steven给出的答案非常中肯。有一个稍微好一点的 API 来做到这一点(在 Windsor 的第 3 版中添加,因此未在上述书籍中涵盖(,而无需使用.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());

最新更新