Consider the following scenario of services and components in a sample C# console application
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...
}
}
Let's imagine to do the following registrations inside the composition root:
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>();
This kind of scenario works fine and I did so several times.
What if, for some reason, I would like to inject inside the constructor of Consumer class only two different implementations of IService, for instance only FooService and BarService (while still continuing to inject all the available implementations of IService inside the constructor of AnotherConsumer) ?
Is there an elegant way to do so?
CodePudding user response:
I grabbed my copy of the first edition of Dependency Injection Principles, Practices, and Patterns. It contains a complete chapter on Castle Windsor and discusses this exact scenario. The trick is to do two things:
- Define the collection registrations as named registrations using
.Named(string)
- Specify an override for
Consumer
using.ServiceOverrides(object)
The following code sample is almost straight out of the book (with the names replaced with your examples):
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>();
CodePudding user response:
The answer that @Steven gave is very much on point. There's a slightly nicer API to do that (added in version 3 of Windsor, so not covered by the aforementioned book) without having to use .Named
or registering all your IService
s one by one.
container.Register(Component
.For<Consumer>()
.DependsOn(
Dependency.OnComponentCollection(
"services", typeof(FooService), typeof(BarService)
)
)
);
Or if you don't want to use the dependency name, you can specify it by type
container.Register(Component
.For<Consumer>()
.DependsOn(
Dependency.OnComponentCollection<IEnumerable<IService>>(
typeof(FooService), typeof(BarService)
)
)
);
With that you can just register your IComponent
s via convention like the following, without any extra fuss.
container.Register(Classes.FromThisAssembly()
.BasedOn<IService>()
.WithServiceBase()
.LifestyleTransient());