Glenn Block

Sponsors

The Lounge

Wicked Cool Jobs

News

  • View Glenn Block's profile on LinkedIn

    Me

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
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 for feedback from a bunch of 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.

Onto 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.


Posted Mon, Feb 9 2009 2:20 AM by Glenn Block

[Advertisement]

Comments

DotNetShoutout wrote CommonServiceLocator for MEF, a service is a service. - Glenn Block
on Mon, Feb 9 2009 5:55 AM

Thank you for submitting this cool story - Trackback from DotNetShoutout

Stephen Taylor wrote re: CommonServiceLocator for MEF, a service is a service.
on Mon, Feb 9 2009 8:35 AM

Yay! I've been wondering why MEF didn't have a CSL impl yet.

Cheers!

Harry M wrote re: CommonServiceLocator for MEF, a service is a service.
on Mon, Feb 9 2009 8:37 AM

Glenn, would you consider adding a DelegateServiceLocator : ServiceLocatorImplBase to the library?

ie.

class DelegateServiceLocator :ServiceLocatorImplBase {

   public DelegateServiceLocator (Func<Type, string, object> resolveObject, Func<Type, IEnumerable<object> resolveAll){

        ...

   }

}

This would save on the additional zillion + 1 servicelocatorimpl libraries floating around

Dew Drop - February 9, 2009 | Alvin Ashcraft's Morning Dew wrote Dew Drop - February 9, 2009 | Alvin Ashcraft's Morning Dew
on Mon, Feb 9 2009 9:20 AM

Pingback from  Dew Drop - February 9, 2009 | Alvin Ashcraft's Morning Dew

Josh wrote re: CommonServiceLocator for MEF, a service is a service.
on Mon, Feb 9 2009 10:57 AM

Not to nitpick, cause I really hate when people do that. But where you're using Count() and First(), wouldn't that be better off as a FirstOrDefault() followed by a null check? In this case, you probably already know what is implementing IEnumerable, but if you didn't, using the two methods separately could trigger at least one complete enumeration and a second that gets the first result.

Reflective Perspective - Chris Alcock » The Morning Brew #283 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #283
on Tue, Feb 10 2009 3:31 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #283

Bringing MEF to Xaml via MarkupExtension « Denis Vuyka’s Weblog wrote Bringing MEF to Xaml via MarkupExtension &laquo; Denis Vuyka&#8217;s Weblog
on Mon, Feb 23 2009 10:44 AM

Pingback from  Bringing MEF to Xaml via MarkupExtension &laquo; Denis Vuyka&#8217;s Weblog

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Devlicio.us