Endjin - Home

Component discovery and composition Part 1c: Fundamentals – Castle.Windsor

by Matthew Adams

Combining MEF With Castle.Windsor For Low-Ceremony Component Composition

Last time, we looked at MEF and what it brings the to party. (The answer was a very powerful composition solution.) This time, we’re going to contrast that with the facilities offered by Castle.Windsor.

Part of the solution — Castle.Windsor

Windsor takes as its primary focus the component container problem. Its usage model, rather than import and export, is based on registration and resolution. Although similar in effect, they are quite different in approach.

To make a component available in the container we call Register(). Here’s an example registering a component called RestStarterKitHttpClient for a service contract defined by the IHttpClient interface. In this case, we’re explicitly stating that we want a new instance every time by specifying a transient lifetime. (We’ll talk more about lifetimes and initialization in another post.)

public interface IHttpClient
{
    HttpResponse Get(Uri uri);
}

public class RestStarterKitHttpClient : IHttpClient
{
    HttpResponse Get(Uri uri)
    {
        // ...
    }
}

container.Register(
    Component.For<IHttpClient>()
    .ImplementedBy<RestStarterKitHttpClient>()
    .LifeTime.Transient);

In order to get hold of the component concerned, I can explicitly call Resolve() Here’s an example of that.

IHttpClient client = container.Resolve<IHttpClient>();

Compare this to our non-IOC equivalent:

IHttpClient client = new RestStarterKitHttpClient();

So you can think of Resolve() as the IOC equivalent of new. They both provide us with instances of an object, but there’s an extra level of indirection which gives us more control over how we construct and return it. Rather than always allocating a brand new instance, it might be a singleton, shared amongst all clients, or something from a pool, for example. It might be a “real” instance (like the RestStarterKitHttpClient) or a mock for testing. The decision is taken at registration time, rather than instantiation time (hence the term Inversion of Control).

Just as with MEF composition, it also cascades component resolution through the dependencies of the resolved items. Where it differs is that, rather than requiring explicit importing attributes to indicate which dependencies should be resolved by the container, it does this by convention.

What does that mean in practice? If my RestStarterKitHttpClient needs some other component to do its job (an ICacheManager, for instance), then I can just extend my plain-old-CLR class like this.

public class RestStarterKitHttpClient : IHttpClient
{
    readonly ICacheManager cache;

    public RestStarterKitHttpClient(ICacheManager cache)
    {
        this.cache = cache;
    }
}

When you Resolve() the IHttpClient, it will look at the RestStarterKitHttpClient component, discover this non-default constructor, look in the container for a component implementing the ICacheManager interface, find it (providing we registered it, of course!) and inject it into the constructor. A very similar pattern to MEF (compare our FireTruck with our RestStarterKitHttpClient), but it leaves our code much cleaner than the equivalent in our MEF sample.

The problem is that we’ve completely lost the dynamic component discovery that MEF gave us; we’re explicitly registering a concrete component in the container. We’d like a better solution.

And that will be the subject of the next post.


What would happen if we hadn’t registered the component? Because we have no default constructor it can fall back to, Windsor would throw a ComponentNotFoundException, and the call to Resolve() would fail.

Matthew Adams on Twitter

About the author

Matthew was CTO of a venture-backed technology start-up in the UK & US for 10 years, and is now a Founder of Endjin Ltd, which provides technology strategy, experience and development services to its clients who are seeking to take advantage of Microsoft Azure and the Cloud. You can follow Matthew on twitter.