Alan Dean

CTO, Developer, Agile Practitioner

Photograph of Alan Dean

Saturday, June 19, 2010

Abstracting Service Location

A couple of days ago I blogged about the Common Service Locator, how to use the ServiceLocator and how to mock the IServiceLocator interface for unit testing purposes. However, I personally feel that the Common Service Locator library didn’t complete the job. Ideally, I should not need to choose specific IoC provider whilst writing my code (or at least should not be forced to recompile my application in order to change provider) and the library doesn’t enable this.

In order to enable this use case, I’ve published a set of lightweight libraries in the Cavity project. The key library is Cavity.ServiceLocation.dll which contains one interface and one concrete class. The interface is, frankly, trivial:

namespace Cavity.Configuration
{
    public interface ISetLocatorProvider
    {
        void Configure();
    }
}

The interface is trivial simply because it’s a hook to load the provider-specific configuration data. I have provided a plain vanilla implementation for Autofac, Castle Windsor, StructureMap and Unity because each of these supports XML configuration. Here is the implementation of the Castle Windsor ISetLocatorProvider:

namespace Cavity.Configuration
{
    using Castle.Windsor;
    using Castle.Windsor.Configuration.Interpreters;
    using CommonServiceLocator.WindsorAdapter;
    using Microsoft.Practices.ServiceLocation;

    public sealed class XmlServiceLocatorProvider : ISetLocatorProvider
    {
        public void Configure()
        {
            var container = new WindsorContainer(new XmlInterpreter());
            ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(container));
        }
    }
}

As you can see, the code is lightweight: create a container, load it with configuration data and apply the configured container to the generic ServiceLocator and you’re done. To set up Castle Windsor as your provider, you must edit your app.config or web.config, as appropriate, as follows (having a separate castle.config is optional but generally considered preferable):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section
            name="castle"
            type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"/>
        <section
            name="serviceLocation"
            type="Cavity.Configuration.ServiceLocation, Cavity.ServiceLocation"/>
    </configSections>
    <castle configSource="castle.config" />
    <serviceLocation type="Cavity.Configuration.XmlServiceLocatorProvider, Cavity.ServiceLocation.CastleWindsor" />
</configuration>

The <serviceLocation> element type attribute points to the ISetLocatorProvider implementation you wish to use. To learn more about castle.config, see Initializing with an external configuration. To see examples of each provider, you can browse the Cavity source code.

If you prefer to specific your container via a fluent interface, then you can still employ the ISetLocatorProvider abstraction by writing a custom implementation.

More detail about using the Cavity.ServiceLocation.dll library can be seen on the Cavity Wiki. Packages for each of the four plain vanilla implementations can be downloaded as zips and the binaries are also available via trove.

No comments: