For a previous project I extended the IoC container Castle Windsor to support dynamic component selection. In this case it was extended to select the correct implementation based on the current user type. I even wrote a CodeProject article about it, but for some reason I never published the article. Well this morning I did submit it and it can be found here. I will try to outline the changes I did in this post as well.

Why do you need dynamic component selection? Well imagine you are going to develop a web application that is used by B2C (Consumer), B2P (Partner) and B2E (Employee) users. The B2C users has their profile and password in a local database, the B2P users profile can only be accessed via an external third party web service and the B2E users are in an LDAP server.

In this kind of scenario you would do an interface for a user repository and then an implementation for each user type. If this web application serves all these user types from a single instance then you need a factory to return the correct implementation based on the current user type. If you have many different scenarios where the implementation will be different based on some common runtime condition then it would be nice to declare that (for example in a xml configuration file) and then have the inversion of control container dynamically select the correct component. This way you would not need to create a bunch of almost identical factories for each scenario.

So what we want to do is basically state in the windsor configuration which implementation coresponds to which user type.

<components>
    <component	id="b2e.user.repository"
		service="ExtendingWindsor.IUserRepository, ExtendingWindsor"
		type="ExtendingWindsor.EmployeeUserRepository, ExtendingWindsor" 
		channel="B2E" />
    
    <component	id="b2c.user.repository"
		service="ExtendingWindsor.IUserRepository, ExtendingWindsor"
		type="ExtendingWindsor.ConsumerUserRepository, ExtendingWindsor" 
		channel="B2C" />

    <component	id="b2p.user.repository"
		service="ExtendingWindsor.IUserRepository, ExtendingWindsor"
		type="ExtendingWindsor.PartnerUserRepository, ExtendingWindsor" 
		channel="B2P" />
</components>

The channel attribute in the above xml is not something that exists in the standard Castle Windsor configuration schema. However Castle Windsor allows for additional attributes so the only thing we need to do now is to extend the container so that when it searches for a implementation of the IUserRepository interface it will take the correct one.

One way to acheive this is to exchange the default naming subsystem with one that inherits from DefaultNamingSubSystem and change the behavior of the GetHandler method.

public class ExtendedNamingSubSystem : DefaultNamingSubSystem
{
    public override IHandler GetHandler(Type service)
    {
        IHandler[] handlers = base.GetHandlers(service);

        if (handlers.Length < 2)
            return base.GetHandler(service);

        UserPrincipal user = (UserPrincipal) Thread.CurrentPrincipal;

        foreach (IHandler handler in handlers)
        {
            string channel = handler.ComponentModel.Configuration.Attributes["channel"];
            if (channel == user.Channel)
            {
                return handler;
            }
        }

        // use default
        return base.GetHandler(service);
    }
}

What we need to do now is to replace the default NamingSubSystem with our own. To exchange the NamingSubSystem of the underlying kernel in Castle Windsor is a little more problematic than it should. We need to use the constructur that takes in an implementation of IKernel.

This is what we have to do:

IKernel kernel = new DefaultKernel();
kernel.AddSubSystem(SubSystemConstants.NamingKey, new ExtendedNamingSubSystem());
XmlInterpreter interpreter = new XmlInterpreter();
DefaultComponentInstaller installer = new DefaultComponentInstaller();

interpreter.Kernel = kernel;
interpreter.ProcessResource(interpreter.Source, kernel.ConfigurationStore);

WindsorContainer container = new WindsorContainer(kernel, installer);
container.Installer.SetUp(container, kernel.ConfigurationStore);

This is setup is a little more complex than what you normaly do:

WindsorContainer container = new WindsorContainer(new XmlInterpreter())

But there is no constructor that takes an IKernel and a IConfigurationInterpreter. I have thought about submitting such a constructor as a patch to the castle team but have not gotten around to it. Anyway now that we are done we can do this:

IUserRepository repos = container.Resolve<IUserRepository>();

The container will dynamically select the correct component based on the current principal. This also works if you have a component that has a constructor dependency to the IUserRepository. You have think about the lifestyle though so that you do not store a reference to a IUserRepository implementation in a singleton component.

Please observe that the above implementation of ExtendedNamingSubSystem is just a proof of concept implementation. For a real implementation you should try to reduce the need to call base.GetHandlers(service).

Update: Instead of extending the DefaultNamingSubSystem yourself you can use the KeySearchNamingSubsystem which is included in the Castle Windsor release. This allows you to use meta data inserted in the component id to do dynamic component selection.