I know I can do it using child containers. If I have the following structure:
parent container
/ \
child container 1 child container 2
then every component I register in "child 1" is hidden from "child 2", while they both can use common components from parent container. I think that is pretty much what I need, however I have read on multiple occasions, that child containers are evil and that there are often a better way to achieve the same behavior. For example here Krzysztof claims, that
Basically handler selectors and sub-resolvers give you all the power you need to handle scenarios where you would want to use child container instead. I think removing the child containers, and add some nicer support for contextual scoping of components would be the best solution.
Are there any examples that back this up? After reading related documentation, I feel that I am still in the dark. I just don't see how to achieve the same behavior using custom selectors and sub-resolvers.
Use case. I have multiple instances of the following component:
class Component
{
public Component(ILayer[] layers, ...)
{
...
}
}
which I want to resolve with a help of default ArrayResolver
. However for each instance of Component
I want to only inject a specific subset of registered layers, that were registered specifically for this component. If I do not use the child containers, the registration will probably look like this:
container.Register(Component.For<ILayer>.ImplementedBy<LayerA>()
.Named("Component1_LayerA"));
container.Register(Component.For<ILayer>.ImplementedBy<LayerB>()
.Named("Component1_LayerB"));
//etc...
container.Register(Component.For<ILayer>.ImplementedBy<LayerB>()
.Named("Component2_LayerB"));
container.Register(Component.For<ILayer>.ImplementedBy<LayerC>()
.Named("Component2_LayerC"));
//etc...
conatiner.Register(Component.For<Component>.Named("Component1"));
conatiner.Register(Component.For<Component>.Named("Component2"));
Now, when I call container.Resolve<Component>("Component1")
how do I tell Windsor to only resolve layers, which name starts with "Component1_"? Or should I use a completely different approach?
Here is a sub-resolver I came up with via trial and error:
class LayersResolver : ISubDependencyResolver
{
public LayersResolver(IKernel kernel)
{
_kernel = kernel;
}
public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model,
DependencyModel dependency)
{
return dependency.TargetType == typeof(ILayer[]);
}
public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model,
DependencyModel dependency)
{
var result = _kernel.GetHandlers(typeof(ILayer))
.Where(h => h.ComponentModel.Name.StartsWith(model.Name))
//at this point it is not clear to me, whether I should call
//h.Resolve(context)
//or
//h.Resolve(context, contextHandlerResolver, model, dependency)
//or
//_kernel.Resolve<ILayer>(h.ComponentModel.Name)
//and what is the difference
.Select(h => _kernel.Resolve<ILayer>(h.ComponentModel.Name))
.ToArray();
return result;
}
private readonly IKernel _kernel;
}
It does seem to work correctly, at least for simplified example in my question. However I cannot confirm that this is the best solution to my problem. As Yacoub Massad mentioned in the comments, this might be one of those cases where it is best to drop the container altogether.