Monday, May 03, 2010

10 Advanced Windsor Tricks – 13. Use SubContainers to replace dependencies on context

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.

2 comments:

Nestor said...

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.

Thanks in advance for you nice article.
Nestor

Mike Hadlow said...

Nestor,
The 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.