Advanced Dependency Injection with Castle Windsor

Another loosely coupled post on Going to Production with MonoRail.

Many moons ago I wrote a post about dynamically resolving a component from the Castle container based on runtime parameters.  There was some discussion about the limitations of the approach (the biggest is that all components that require customization or components that have dependencies that require customization must be Transient) but overall it accomplished my goals at the time.  To summarize, my implementation allowed you to load multiple components that implement the same service (say an IOrderValidator) and also allowed you to provided a predicate that would be invoked when your code resolved an IOrderValidator to allow you to choose which specific implementation to return.  The idea was that you could have multiple clients hosted on a single instance of your application, each requiring their own order validation logic.  Using a runtime parameter (like the host header) your provided predicate would resolve the IOrderValidator associated with the particular client's requirements.  This implementation worked for some time but had a few limitations, namely all components for all clients had to be loaded into the same container.  This ended up being a tad unruly to manage.  There were also some limitations on the order in which components were loaded into the container, which was problematic when auto configuring the container via code or something like Binsor.

I set out for a better solution that addressed my new requirements and ran into the related issue of scoped containers that had been proposed on the Castle mailing lists.  Scoped containers have a slightly different use case and requirements but overlapped some with the approach I ultimately settled upon.  My container contains over thirty (30) components that make up the core functionality of the application.  Some of these components have dependencies on other components and all of this management, injection, and resolution is handled nicely and elegantly by the container.  Lets say for a particular client I need to customize one of the these components.  But the component in question has dependencies on three other components, whose functionality I do not need to customize.  I need a way to add my customized component to the container, but still resolve its dependencies from the core set of components.  The answer to my problem came in the form of Child Containers.

A child container is just like your standard container only that it has an associated parent.  When you try to resolve a component from a child container, it looks at its own components to fulfil dependencies and if it doesn't find it, it asks the parent container if it can fulfil the dependency.  This sounds like just the solution I needed.  I can load all of my core components into the parent container, then load any custom components into a child container.  One glaring flaw though.  Lets say I have component A that has a dependency on component B.  I have a core implementation of both components in my parent container.  I have a custom implementation of component B in my child container.  I attempt to resolve component A from the child container.  It is not present so it is retrieved from the parent container.  When resolving the dependency of component B, the parent uses the core implementation, not the custom implementation in the child container.  I considered this a bug in the dependency resolution code and you can read about the ensuing discussion on the Castle mailing list.  This issue was also reported by Rinat Adbullin a few months back and prompted him to choose another container implementation.  I submitted a patch for my implementation but as far as I know it has not been applied.  I've heard Hammett say that child containers are the devil (my words, not his) I believe because they can introduce difficult to diagnose bugs and make dependency resolution more complicated.  For my problem they have provided an elegant solution.

To bring this implementation into MonoRail was simply a matter of crafting a custom HttpApplication that implements IContainerAccessor, and returning the appropriate child container based on the host header of the current request.  I have the base container that contains all of the core components.  I create a child container with the base as its parent for each client and load the appropriate components.  MonoRail can be configured to use Windsor to resolve all of its internal components (including your controllers).  MonoRail calls IContainerAccessor.Container to obtain the container to use for its resolution.  By providing the appropriate child container to this call, I can ensure that any custom components for the particular client are used in place of the core components.  By using the dependency resolution patch discussed above, I ensure that any core component dependencies are properly resolved against any existing customized components in the child container (code sample below).

To sweeten the deal, child containers are configured using a simple text file that can be modified at runtime to turn individual custom components on or off on a per client basis, without restarting the application.  More on that in my future post on managing configuration.

namespace My.Application
{
  public class CustomHttpApplication : HttpApplication, IContainerAccessor
  {
    private static IWindsorContainer _baseContainer;

    private static readonly Hashtable _configToContainer = new Hashtable();

    protected virtual void Application_Start(object sender, EventArgs e)
    {
      _baseContainer = new CustomContainer();
    }

    public IWindsorContainer Container
    {
      get { return GetContainer(); }
    }

    protected virtual IWindsorContainer GetContainer()
    {
      string clientId = ClientIdProvider.Get();

      if (string.IsNullOrEmpty(clientId))
      {
        return _baseContainer;
      }

      ClientConfiguration clientConfig = ClientConfigurationManager.GetByClientId(clientId);

      IWindsorContainer clientContainer = null;

      if (!_configToContainer.ContainsKey(clientConfig.Path))
      {
        clientContainer = new CustomContainer(clientConfig, clientId, _baseContainer);
        _configToContainer.Add(clientConfig.Path, clientContainer);
      }
      else
      {
        clientContainer = (IWindsorContainer)_configToContainer[clientConfig.Path];
      }

      return clientContainer;
    }
  }
}
«June»
SunMonTueWedThuFriSat
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345