Here’s part thirteen of (slightly more than) 10 Advanced Windsor Tricks.
Say you have a multi-tenanted application and you want to have different components provided for different customers. One way of doing this is to create an IHandlerSelector which allows you to choose which components get provided depending on some context. It’s the technique I use for Suteki Shop’s multi-tenancy and I’ve written about it several times in the past: here and here.
An alternative approach is to use a SubContainer. SubContainers are a way to provide separate scoping and resolution, independent of your main container. In this scenario (described here by Bill Pierce) you provide a SubContainer for each tenant, and simply register the components that that tenant needs that are different from the default.
Here’s a simple demonstration:
public void SubContainer_should_get_components_of_parent()
{
var parentContainer = new WindsorContainer()
.Register(
Component.For<Root>().LifeStyle.Transient,
Component.For<ChildNode>().LifeStyle.Transient,
Component.For<IGrandChild>().ImplementedBy<GrandChild>().LifeStyle.Transient
);
var subContainer1 = new WindsorContainer()
.Register(
Component.For<IGrandChild>().ImplementedBy<FirstGrandChild>().LifeStyle.Transient
);
var subContainer2 = new WindsorContainer()
.Register(
Component.For<IGrandChild>().ImplementedBy<SecondGrandChild>().LifeStyle.Transient
);
parentContainer.AddChildContainer(subContainer1);
parentContainer.AddChildContainer(subContainer2);
var parentRoot = parentContainer.Resolve<Root>();
parentRoot.Accept(node => Console.WriteLine("Resolved from parent container: {0}", node.GetType().Name));
var child1Root = subContainer1.Resolve<Root>();
child1Root.Accept(node => Console.WriteLine("Resolved from subContainer1: {0}", node.GetType().Name));
var child2Root = subContainer2.Resolve<Root>();
child2Root.Accept(node => Console.WriteLine("Resolved from subContainer2: {0}", node.GetType().Name));
}
Which outputs:
Resolved from parent container: Root
Resolved from parent container: ChildNode
Resolved from parent container: GrandChild
Resolved from subContainer1: Root
Resolved from subContainer1: ChildNode
Resolved from subContainer1: FirstGrandChild
Resolved from subContainer2: Root
Resolved from subContainer2: ChildNode
Resolved from subContainer2: SecondGrandChild
I’m creating a parent container and registering three components, Root, ChildNode and IGrandChild. Root depends on ChildNode and ChildNode depends on IGrandChild. I then create two subContainers and register alternative IGrandChild components for each.
I’ve got a little visitor pattern thing going to run some function over each object in the graph so that I output the component types.
When I resolve Root from the parent container, I simply get an object graph with IGrandChild implemented by GrandChild. So my default implementation works as expected.
The really cool stuff happens when I resolve Root from the sub containers. Root is not registered with the sub containers, so they resolve it from their parent (parentContainer). The same goes for ChildNode. But the IGrandChild dependency is satisfied by the sub container’s registration. With this technique you can surgically replace specific dependencies.