Hierarchical Containers

time to read 3 min | 524 words

This is somewhat of a specific scenario, but let us assume that you have an application where you want to specialize the services of the applications by the current user. If the user belongs to the Northwind customer, you want to have one behavior, and if it belongs to the Southsand customer, you want to have a different behavior. All users from all others customers get the default behavior.

To make it simple, let us talk about NHibernate configuration. You have a default schema that you use for most customers, and you specialize that for those customers that wants extra. This means that you need to keep a session factory per customer, because you have different schema that in the default one (changing the connection string is not enough).

To be clear, this is not about entity inheritance, this is about specialization of the entire application, which I just happened to demonstrate via NH configuration.

Now, Windsor supports this ability by having a parent container and child containers, but Binsor didn't expose this functionality easily, I did some work on it today, and the end result is that you can configure it like this:

We have the global container (implicit to Binsor, since we created it before we run the Binsor script), then we run over the configuration files and create a container per each file. We register them in the ContainerSelector, which is an application level service (below).

import HierarchicalContainers
import System.IO

Component("nhibernate_unit_of_work", IUnitOfWorkFactory, NHibernateUnitOfWorkFactory,
	configurationFileName: """..\..\hibernate.cfg.xml""")
	
Component("nhibernate_repository", IRepository, NHRepository)
Component("container_selector", ContainerSelector)

for configFile in Directory.GetFiles("""..\..\Configurations""", "*.cfg.xml"):
	continue if Path.GetFileName(configFile) == "hibernate.cfg.xml"
	print "Build child configuration for ${configFile}"
	child = RhinoContainer(IoC.Container)
	using IoC.UseLocalContainer(child):
		Component("nhibernate_unit_of_work", IUnitOfWorkFactory, NHibernateUnitOfWorkFactory,
			configurationFileName: configFile)
		Component("nhibernate_repository", IRepository, NHRepository)
	#need to remove both .cfg and .xml
	containerName = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(configFile))
	IoC.Container.Resolve(ContainerSelector).Register(containerName, child)

You can use it like this, and enter/leave the context of the a client at will:

RhinoContainer container = new RhinoContainer("Windsor.boo");
IoC.Initialize(container);
ContainerSelector containerSelector = IoC.Resolve<ContainerSelector>();
containerSelector.PrintChildContainers();
using(UnitOfWork.Start())
{
    Console.WriteLine(
        NHibernateUnitOfWorkFactory.CurrentNHibernateSession
            .Connection.ConnectionString
        );
}
using(containerSelector.Enter("Northwind"))
{
    using (UnitOfWork.Start())
    {
        Console.WriteLine(
            NHibernateUnitOfWorkFactory.CurrentNHibernateSession
                .Connection.ConnectionString
            );
    }
}
using (containerSelector.Enter("Southsand"))
{
    using (UnitOfWork.Start())
    {
        Console.WriteLine(
            NHibernateUnitOfWorkFactory.CurrentNHibernateSession
                .Connection.ConnectionString
            );
    }
}

Because this is a fairly complex topic, I have created a simple reference implementation that you can get here:

https://rhino-tools.svn.sourceforge.net/svnroot/rhino-tools/trunk/SampleApplications/HierarchicalContainers