Combining MEF With Castle.Windsor For Low-Ceremony Component Composition
Registration by convention
In the last part of this series, we looked at the basic features of Castle.Windsor, and saw how we can register types explicitly in the container.
Rather than having to explicitly register everything, we'd like somehow just to know what we want to install in the container. To do that, we can define conventions which, if our components adhere to them, will get them into the container appropriately.
What sort of conventions are we talking about? Well, we've already seen MEF use a convention-based model: the convention is to add an [Export] attribute. This is a high-ceremony convention, in that it adds something to your code to explicitly participate in the registration, and for no other reason.
What we want is a low-ceremony convention. Good examples might be "lives in a particular namespace" or "implements a particular interface".
Windsor offers us a very nice fluent API for this pattern of registration.
Here, the convention is that we should install types that implement the IMyServiceContract interface, either directly:
We are also using Windsor's built-in discovery mechanism to say where we are looking for these types. In this case, we are looking in the assembly containingSomeMarkerType. There are also ways of specifying assemblies in a particular directory, just as with MEF, but there is no out-of-the-box support for XAP packages or re-composition.
Using the marker type is a very simple but expressive way of identifying a specific assembly, but it does mean that we are taking a dependency on the marker type as we do so. Later on, we'll look at how we can take advantage of this simplicity, but without taking the dependency (in our main bootstrapper, at least).
Here's another example that uses namespace matching to find the components to register.
Here we're registering anything in a namespace that contains the word "Tasks", and using its default interface as the service contract. So this would find and install a class like this:
Or like this:
With these two basic conventions in place, our developers can get their components installed just by implementing their service contract (which they would have had to do anyway, of course) or by putting them in a well-known namespace. All our tasks could live in Endjin.MyComponent.Tasks, for example, and the registration call above would find them and put them in the container without further ceremony.
Clearly, we could write more of these to codify other conventions (by type names, for instance, or requiring that they implement a method with a particular name).
Best of all, our bootstrapper doesn't need an explicit reference to each and every component to register; it just installs everything that conforms to the convention. This means we've successfully decoupled the container bootstrapper from the components it is going to install (just like MEF); but, unlike MEF, we haven't added any additional ceremony in the process. Our code looks exactly like it would have done had we not been using the container.
So, we now have a means of registering components into the Windsor Container by convention, but we'd like the flexible means of discovery (and Silverlight XAP support) we get with the MEF catalog. We can achieve this by leveraging another Windsor feature — the installer. Which is for the next part.