bling.github.io

This blog has relocated to bling.github.io.

Monday, October 31, 2011

My Thoughts on MEF

Ever since MEF was conceived, despite the authors saying that it is not an IoC container, it has since evolved to become one of the more popular IoC containers.  I’ve always avoided it because I disagree with using attributes, and I’ve had no reason to use it over Autofac or Windsor.

Recently, I found a reason to use it – Metro-style applications only support MEF so far.  My Twitter client ping.pong uses Autofac as the IoC container.  It uses some very basic functionality like factories and hooks.  To my surprise, MEF has no support for either of these.

Coming across these limitations solidifies my opinion that MEF is a plugin container, not an IoC container.

First let’s take a look at automated factories. What I mean is that by registering Foo, like so:

container.RegisterType<Foo>();

the container will automatically provide us a Func<Foo> without explicitly having to register it. This can be useful when you want to create an instance of Foo some time in the future rather than at constructor time.  You can do this with MEF via an ExportFactory<T>, but it’s limited because you cannot override dependencies at resolve time.

For example, let’s say Foo has a constructor of Foo(Bar1, Bar2, Bar3). With MEF, you have no control at resolution time what the Bars are. A container that has support for automated factories (like Autofac and Castle Windsor), will let you resolve a Func<Bar1, Foo>, which lets you override Bar1 at resolve time. Similarly, you can resolve a Func<Bar1, Bar2, Bar3, Foo> and override all dependencies. Any dependencies not overridden fall back to their configuration in the bootstrapper. This is a very useful feature, and coupled with the scoping features for automatic disposal it opens up many doors for elegant solutions for what otherwise are complicated problems.

On to the second point; MEF has limited extension points. This one sounds odd since MEF is all about designing decoupled plugins so surely it should have extension points! The problem here is that MEF is designed as an explicit API (attributes are required) rather than an implicit API. In Autofac, you can scan an assembly and register every type. In MEF, every class needs to have an [Export] on it.  It also baffles my mind why [ImportingConstructor] is required even when there’s only one constructor. All this explicitness means you lose a bunch of “free” extension points that typical IoC containers have, like this:

b.RegisterAssemblyTypes(GetType().Assembly)
  .OnActivated(x => x.Context.Resolve<IEventAggregator>().Subscribe(x.Instance));

What the code above is saying that every time any component is activated, it will subscribe to the event aggregator. If the component doesn’t IHandle<> any messages, it’s a no-op and continues on. If the instance does IHandle<> messages, this will ensure it’s hooked up.

The closest thing I could find in MEF was IPartImportsSatisfiedNotification (yes, an interface, more explicitness!).  It contains a single method OnImportsSatisfied() which gets called when the part is created.  Needless to say, the one line of code from Autofac would translate into a method for every implementation of IHandle<>, and since OnImportsSatisfied() contains no contextual information, every component will need IEventAggregator injected just to be able to call Subscribe.

To fully complete this example, Autofac has the following methods when registering a component: OnRegistered, OnPreparing, OnActivating, OnActivated, and OnRelease.  Each of these methods gives you complete contextual information at the time it is called like access to the current scope of the container, the instance (if applicable), which component which requested the dependency, etc.  This makes it almost too easy to extend the container.

For MEF, the only real extension point is an ExportProvider.  It is pretty low level (all it does is parse attributes for you) so to write anything similar for MEF requires a lot more code.  To further illustrate this point, compare the interception modules from AutofacContrib and MefContrib.  The Autofac implementation is a single file with a couple extension methods.  The MEF implementation is an entire namespace, over multiple classes, not the mention that it also relies on other infrastructure code in MefContrib.  Basically, the guys that wrote MefContrib had to write a mini-container within MEF.

MEF is great for building extremely loosely coupled applications.  I don’t think it has any business in an application where you know and own all of the dependencies; there are simply better libraries for that.

2 comments:

Nick said...

Hi Bailey! Nice thoughtful article - I have a foot in both camps, so I'm glad that the Autofac extension points work for you, but I'm also keen to close some of the gaps on the MEF side :)

I am curious, have you had a chance to look at RegistrationBuilder, being delivered in the .NET 4.5 version of MEF? It opens up the possibility for us to support activation events like the ones you mention, although it doesn't as of writing.

In case you didn't catch it, the release announcement is at: http://blogs.msdn.com/b/bclteam/archive/2011/10/27/what-s-new-in-mef-version-2-preview-4.aspx

Thanks for writing this up anyway, it will give us something to think about.

Regards,
Nick

bling said...

Thanks for the reply Nick!

I have yet to check out all the new MEF features of 4.5, but it definitely looks very interesting with what's happening in the previews.

The new RegistrationBuilder looks pretty good, especially since attributes are optional now, but that only solves wiring up the application.

IMO, the most useful (and powerful) thing you can add is the equivalent of Autofac's IComponentContext -- something that will let you easily hook into any part of the resolution chain from creation to disposal.

Once that's in it'll be much easier to extend the container and use it for more "advanced" scenarios --and with less code.

Cheers,
Bailey