Friday, November 28, 2008

Using the Windsor Fluent Interface and an XML Configuration file

The framework we've built uses the Windsor container quite heavily. Users of the framework supplied a dependencies.config file in their application that had a bunch of boilerplate stuff in it for our framework (facilities, framework component registrations, etc.) along with the registration for all of their components.

Boilerplate anything bothers me and boilerplate XML even more so. I wanted a nice programmatic way for the users to say "configure the framework" in their application as a substitute for all the XML. What this meant, however, was that I now needed to configure the container 2 ways: programmatically using the fluent interface available in the trunk, and using a config file.

My first attempt went something like this:

WindsorContainer c = new WindsorContainer("dependencies.config");
c.Register(Component.For<IFrameworkThing>().ImplementedBy<FrameworkThing>();
c.AddFacility<RequiredFacility>();


While that looks ok, the order things happened bit me. Our facility (RequiredFacility in this example) happens to listen to the ComponentModelCreated event raised by the Kernel when a component is added to add a ComponentActivator (more specifics here). Since the XML gets processed (and all the components in it get added) before our facility is added, we miss the events we need to handle.

I needed a way to have my facility in place before adding in the stuff in the config. Due to a lack of documentation and the castle forums guys taking Thanksgiving off, I went down a bunch of fruitless avenues. I looked into loading the framework stuff into a parent container and then creating a child container with

WindsorContainer child =
new WindsorContainer(
parentContainerAlreadyConfigured,
"dependencies.config");


but the parent doesn't get the ComponentModelCreated events from the child.

Eventually I ended up with the following solution. This came from using Reflector (the best documentation for Castle I've found to date) and mimicking what the WindsorContainer constructor itself does.


WindsorContainer container = new WindsorContainer();
container.AddFacility<RequiredFacility>();
//other programmatic config
XmlInterpreter configInterpreter =
new XmlInterpreter("dependencies.config");
configInterpreter.ProcessResource(
configInterpreter.Source,
container.Kernel.ConfigurationStore);
container.Installer.SetUp(
container,
container.Kernel.ConfigurationStore);



The API seems a bit ugly, but it works. I get all the events I need and the users of the framework don't have to deal with any more boilerplate Windsor configuration.

Update: It turns out you can replace all the XmlInterpreter stuff with this:

container.Install(
Configuration.FromXmlFile("dependencies.config"));

This does the same stuff, only it looks a lot nicer.