CommonServiceLocator for MEF, a service is a service.

Today, I finally got around to uploading a CommonServiceLocator adapter for MEF.

The code is actually quite simple thanks to Chris Tavares providing ServiceLocatorImplBase. (Updated thanks to feedback from several folks)

 public class MefServiceLocator : ServiceLocatorImplBase
{
    private ExportProvider _provider;

    public MefServiceLocator(ExportProvider provider)
    {
        _provider = provider;
    }

    protected override object DoGetInstance(Type serviceType, string key)
    {
        IEnumerable<Export<object>> exports;
        string contract = CompositionServices.GetContractName(serviceType);
        exports = key == null ? _provider.GetExports<object>(contract) : _provider.GetExports<object>(key);

        if (exports.Any())
            return exports.First().GetExportedObject();
        
        throw new ActivationException(string.Format("Could not locate any instances of contract {0}", key));

    }

    protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
    {
        var exports = _provider.GetExportedObjects<object>(CompositionServices.GetContractName(serviceType));
        return exports;
    }
}

One thing you’ll notice is that in DoGetInstance, I am calling to GetExports and then manufacturing only the first export that I return. The reasoning for this is that MEF doesn’t have an internal notion of default. If I call GetExportedObject and more than one is returned, it will blow up with a composition exception. The reason is because we have no way of knowing which is the default as we simply grab parts from catalogs (In Preview 3 we actually provided a way to assign defaults through usage of Export Providers however that’s another story for another overdue post :-) ). So instead of calling for a single, I call for multiple which will succeed in all cases.

To make the adapter satisfy the needs of the interface, I decided to apply Krysztof’s rule of thumb which is to take the first one that is returned regardless, as he says, if I walk into a car dealership and say give me a Saab, I don’t care which. Calling for an export instead of an exported object allows me to only instantiate (and load if I am caching) the one that is being returned.

Another thing you will notice is that i am passing object for the type as DoGetInstance only passes a type. The methods for retrieval on MEF’s Export Provider all allow you to pass object as the type, thus allowing me to do what I need to do.

Finally you’ll notice that I have passed in an ExportProvider to the constructor rather than a container. MEF’s composition container is an ExportProvider. ExportProviders are simply for retrieving exports, which matches very well with ISL’s needs and makes it a good fit.

You can download it here along with unit tests. Thanks to Rob Eisenberg for the initiative which encouraged me to actually get it done.

On to CommonServiceLocator itself…and MEF usage

A while ago , I posted about the new CommonServiceLocator library (and IServiceLocator interface) a bunch of us co-designed and p&p developed. The purpose of this library was to allow applications to use a standard interface for accessing services without being bound to a specific service provider / container.

Once the library launched on CodePlex it caused a healthy amt of debate. Plenty of folks were wondering “Why Service Locator?”. Was this an encouragement to throw away DI containers, and take a trip back in time to the good old days of the Service Locator pattern?  Was this the one abstraction to rule them all?

The answer on both accounts was no. In terms of the first question, the primary place where we thought CSL would be used was for actually accessing a DI container.  That being said, nothing on the interface had anything particularly DI-ish about it. Instead it simply contained interfaces for querying for a service. Now, the thing that provides the service which sits behind the interface might very well be an IoC container which constructs components and  performs dependency injection. However, there is nothing about the interface that enforces that so we felt we were staying true to the api itself.  Furthermore when you access an IoC container and call a Resolve / Get method you ARE performing Service Location. As I like to say, ServiceLocator is the Gateway to an IoC container.

Additionally, wee did not intend IServiceLocator  calls to be scattered throughout the application thereby creating a ton of static dependencies. Instead, the intent was that ISL should be used minimally at the root of an application to compose a root graph, or low-level in framework libraries that were reused across applications.

Now on to the second question. IServiceLocator does one thing, resolve services. We deliberately kept the interface focused around the common set of functionality that service locator’s provide. We kept out anything that was container specific, and also kept away from anything other than retrieval. The reasoning for this was so that this interface did not become some uber generic abstraction with a lot of leaky abstractions. Instead we kept short and sweet and focused on a single concern, resolving.

So that leads to MEF. Why should one want to use ISL with MEF? Quite simply because in the same way that IoC containers can provide services, MEF also provides services. How it find them and how they are created is quite different, but in the end a service is a service.