Meffing with Castle Windsor

Patrik Hägne has an interesting post on removing compile time dependencies between assemblies, while still reaping benefits of using fluent interfaces to bootstrap AutoFac container (go read it, I’m not going to reiterate what Patrick wrote) using MEF.

It inspired me to do similar thing for Castle Winsor.

meffing_with_castle_project

I created a simple solution with 3 project. One is the main application entry, which also holds a MefInstaller which we’ll talk about in a second. Second one: Services, contains interfaces that our application rely on. Impl contains implementation of these interfaces. Pretty simple huh?

The important thing is the tree of dependencies:

meffing_with_castle_dependencies

Notice that there’s no hard dependency on Impl module.

IBar and IFoo along with their implementation are pretty trivial and not really worth showing. Two interesting classes are MefInstaller and ModuleInstaller.

public class MefInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        // NOTE: directory could come from configuration, or any other place
        var directory = Environment.CurrentDirectory;
        
        var compositionContainer = new CompositionContainer(new DirectoryCatalog(directory));
        var installers = compositionContainer.GetExportedObjects<IWindsorInstaller>("ComponentInstaller");
        
        foreach(var installer in installers)
            installer.Install(container, store);
    }
}

Castle Windsor exposes a IWindsorInstaller interface that you can use to batch register whole subsystems or modules with the container. In this case MefInstaller acts as a dynamic composite, which using MEF gathers all IWindsorInstallers that export themselves as “ComponentInstaller” (it’s just a convention I came up with here, this is not mandatory, and doesn’t have any deeper meaning), and then installs them with the container.

[Export("ComponentInstaller")]
public class ModuleInstaller:IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddComponent<IFoo, DefaultFoo>("foo");
        container.AddComponent<IBar, BarImpl>("bar");
    }
}

ModuleInstaller is just one such installer, which registers services implemented in its module.

Now the actual bootstrapping is really simple:

class Program
{
    static void Main(string[] args)
    {
        var container = BootstrapContainer();
        var app = container.Resolve<MyApp>("app");
        app.Run();
        Console.ReadKey(true);
    }
 
    private static IWindsorContainer BootstrapContainer()
    {
        var container = new WindsorContainer();
        container.Install(new MefInstaller());
        container.AddComponent<MyApp>("app");
        return container;
    }
}

BootstrapContainer runs the MefInstaller, and registers MyApp class, which is the main class in our application. Then the class is requested from the container, and a method is invoked on its instance.

The class itself is trivial. What is interesting, is that it has dependencies on two services we defined: IFoo and IBar. Just for kicks, I defined one as constructor dependency, and one as property dependency.

public class MyApp
{
    private readonly IFoo _foo;
 
    public IBar Bar { get; set; }
 
    public MyApp(IFoo foo)
    {
        _foo = foo;
    }
 
    public void Run()
    {
        Console.WriteLine("My Foo: {0}", _foo.Foo());
        Console.WriteLine("My Bar: {0}", Bar.Bar());
    }
}

Now, when we compile the application, and copy MeffingWithCode.Impl.dll to the output directory, because it’s not statically linked to the other assemblies, and run it, we’ll see that MEF does indeed pick up our missing dependencies, so that we don’t get NullReferenceException.

With this we get best of both worlds: We get flexibility of XML configuration (without being prone to typing mistakes), and strongly typed, refactoring friendly registration (without static linking between assemblies).

meffing_with_castle_app

Technorati Tags: , ,

Comments

Julian Birch says:

It’s a pretty interesting problem, but using MEF to solve it doesn’t really feel right to me. Historically, we’ve dealt with the problem of having to reference everything with XML files (or some boo, these days). I’m not sure we’re really after type safety, but auto-complete. We could verify the validity of our configurations at compile time in any event. Really the only problem with compiling a single CS file is sorting out the references, and I think the boo approach to imports (with a couple of well-chosen comments) would achieve quite a lot.

I keep meaning to implement something like this… maybe after I’ve finished the AutoGen patch. 🙂

@Julian
This is kind of problems Mef is _made_ to solve. What doesn’t feel right to you about this?

Paul Batum says:

What tool did you use to generate that dependency graph?