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.
I understand and like the idea of using subcontainer for resolving contextual dependencies, the question is you show direct reference the subcontainer instances, what would be the way to go for having a per tenant subcontainer and referencing it by common code, could be a list of subcontainer and get by some filter the correct subcontainer.
ReplyDeleteThanks in advance for you nice article.
Nestor
Nestor,
ReplyDeleteThe trick would be to have some piece of framework keep a collection of subcontainers per tenant as you say and have them selected per request. The obvious place to do this (in an ASP.NET MVC application) would be the controller factory.
TBH, I haven't tried this in a real application although Bill Pierce (quoted in the post) seems have had good results from it.
Hi Mike,
ReplyDeleteDoes the child container trick you mentioned in this post still working in Windsor 4.0 or above? I tried to implement a similar function. But the components registered in my child container can not override the counterpart in the parent container.
Thanks,
David