Click here to Skip to main content
6,595,444 members and growing! (21,407 online)
Email Password   helpLost your password?
Development Lifecycle » Design and Architecture » Frameworks     Intermediate License: The Code Project Open License (CPOL)

Extending Castle Windsor with dynamic component selection

By Torkel Ödegaard

Demonstrates how you can extend the Castle Windsor container to dynamically select a component based on some runtime context.
C# (C# 1.0, C# 2.0, C# 3.0), .NET (.NET 2.0), Architect, Dev, Design
Posted:4 Apr 2008
Views:6,671
Bookmarked:9 times
Unedited contribution
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
6 votes for this article.
Popularity: 1.73 Rating: 2.22 out of 5
1 vote, 25.0%
1

2

3
1 vote, 25.0%
4
2 votes, 50.0%
5

Introduction

In this article I will try to show how you can extend the Castle Windsor inversion of control container to dynamically select a component based on the current user type. For example if we were 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. This web application serves all these user types from a single instance so the selection of the correct implementation needs to be done at runtime. The solution to that is of course to create a factory that returns 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.

Background

Castle Windsor is a inversion of control container that allows you to configure your components and their dependencies in a xml configuration file.

For more info on inversion of control and Castle Windsor: Oren Eini (Ayende) wrote an excellent article on msdn.

What we want

In Castle Windsor you can configure your components by using xml. So it would be good if you could declare the user type a component is ment for in the xml. for example:
<castle>
    <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>        
</castle>

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 is to extend the container so that when it searches for a implementation of the IUserRepository interface it will choose the correct one.

This is done in the GetHandler method in the NamingSubSystem so what we need to do is inherit from the DefaultNamingSubSystem and override this method.

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 do this we need to look a little at the internals of Castle Windsor and we see that it is actually a wrapper around the Castle MicroKernel container. To exchange the NamingSubSystem of the underlying kernel in Castle Windsor is a little more problematic than it should because we can no longer use the nice constructur that just take in the the path to the xml file. 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.

We are done

Now 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 to think about the lifestyle though so that you do not store a reference to a IUserRepository implementation in a singleton component.

Alternative approach

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.

History

  • 2008-04-01 - Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Torkel Ödegaard


Member
My blog can be found at: codinginstinct.com

Occupation: Web Developer
Company: Cybercom Group
Location: Sweden Sweden

Other popular Design and Architecture articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
  (Refresh) 
-- There are no messages in this forum --

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 4 Apr 2008
Editor:
Copyright 2008 by Torkel Ödegaard
Everything else Copyright © CodeProject, 1999-2009
Web22 | Advertise on the Code Project