Managed Extensibility Framework (MEF) at a Glance

After Krysztof Cwalina has announced Microsoft’s plans on releasing extensible features to .NET framework, the CTP of MEF has made its way too quick into the market. As a result of this, we have an immature .DLL called ComponentModel.dll born into our hands, with being far from complementing the community’s needs and lacking lots of features & architectural concerns – and of course you know it, with nearly no xml comments and not very informative error messages.

But it is still CTP, and that’s what a CTP is for. Criticism against it is a matter of another post; but as .NETters we need to know about this vibe coming because this young baby is going to be a part of the core framework and one day, with an update, will be pushed to x million of computers.

So I better stop judging for the time being and let’s get our hands dirty with what we have currently as early adopters.

Dependency Injection with Management Extensibility Framework

In the dll, there are 2 main namespaces coming: System.ComponentModel.Composition and System.Reflection.StructuredValues . So as you see, to do its magic MEF uses reflection. This also means that one can define contracts using hard coded strings instead of a strongly typed contract. In the examples those are shipped with MEF, you can see this usage, but I think everybody will agree that this is not a good practice. So let’s see how we can build a strongly typed and contract based dependency injection mechanism.

So let’s get to define a very simple contract:

   1: /// <summary>
   2: /// Contract for retrieving pretty dummy data
   3: /// </summary>
   4: public interface IDataRetriever
   5: {
   6:     /// <summary>
   7:     /// Gets the sample data.
   8:     /// </summary>
   9:     /// <param name=”count”>The count.</param>
  10:     /// <returns></returns>
  11:     IEnumerable<ExampleData> GetSampleData(int count);
  12: }

This expects to get a list of example data #count times. ExampleData is a Poco, and its structure contains just a string key:

   1: /// <summary>
   2: /// Model for Sample Data
   3: /// </summary>
   4: public class ExampleData
   5: {
   6:     /// <summary>
   7:     /// Gets or sets the data key.
   8:     /// </summary>
   9:     /// <value>The data key.</value>
  10:     public string DataKey
  11:     {
  12:         get;
  13:         set;
  14:     }
  15: }

So we expect from this method to return a list of ExampleData , with their DataKey field populated by their indexes. So here is the test that ensures this basic expectation:

   1: /// <summary>
   2: ///A test for GetSampleData
   3: ///</summary>
   4: [TestMethod()]
   5: public void GetSampleDataTest()
   6: {
   7:     IDataRetriever target = new DataRetriever();
   8:     int expectedCount, count;
   9:     expectedCount = count = 10;
  10:
  11:     IEnumerable<ExampleData> actual = target.GetSampleData(count);
  12:     Assert.AreEqual(expectedCount, actual.Count<ExampleData>());
  13:
  14:     IEnumerator<ExampleData> enumerator = actual.GetEnumerator();
  15:     int expectedKey = 0;
  16:     while (enumerator.MoveNext())
  17:     {
  18:         Assert.IsNotNull(enumerator.Current);
  19:         Assert.AreEqual(enumerator.Current.DataKey, expectedKey.ToString());
  20:         expectedKey++;
  21:     }
  22: }

And after a couple of failures (yes, even in this simplicity I manage to fail), here is the implementation that passes this test:

   1: [Export(typeof(IDataRetriever))]
   2: public class DataRetriever : IDataRetriever
   3: {
   4:     #region IDataRetriever Members
   5:
   6:     //[Export(”Retriever”)]
   7:     public IEnumerable<ExampleData> GetSampleData(int count)
   8:     {
   9:         List<ExampleData> retVal = new List<ExampleData>(count);
  10:         for (int i = 0; i < count; i++)
  11:         {
  12:             yield return new ExampleData()
  13:             {
  14:                  DataKey = i.ToString()
  15:             };
  16:         }
  17:     }
  18:
  19:     #endregion
  20: }

Now, the syntax of MEF needs us to shout what we have, and explicitly define by attributes what we want to expose as our services to be injected (intrusive, girrrr…) And since there is an exporter, there should be an importer too, which is a page in this scenario. Beware, the page is using this interface and it is marked as public, as MEF can inject only public dependencies choosing the same way as the many of the other IOC containers in the wild.

   1: [Import(typeof(IDataRetriever), IsOptional = false)]
   2: public IDataRetriever Retriever
   3: {
   4:   get;
   5:   set;
   6: }

Note that these Import and Export attributes are under System.ComponentModel.Composition namespace and they both have another overload that takes strings as contract names instead of contract types as shown above.

Please also note that as a client to DataRetriever, this page doesn’t know any bit about which implementation of IDataRetriever that it will retrieve (DI mission accomplished). So in which house is all the party happening? I placed an initialization code inside the page constructor:

   1: public _Default()
   2: {
   3:     DataRetrieverHelper.InitializeContainer<_Default>(this);
   4: }

And this helper is a very smart guy who knows about everything about this magic (so from what we learnt from Italian mafia movies, it should be killed – by a DSL or an XML configuration. But MEF doesn’t support it currently out of the box – but with a bit of a hack it can be done):

   1: public static class DataRetrieverHelper
   2: {
   3:     public static void InitializeContainer<T>(T toFillDependency)
   4:         where T:class
   5:     {
   6:         CompositionContainer container = new CompositionContainer();
   7:         container.AddComponent<T>(toFillDependency);
   8:         container.AddComponent<DataRetriever>(new DataRetriever());
   9:         container.Bind();
  10:     }
  11: }

As you see, you add the consumer, add the service, and call bind – MEF container cares the rest.

Of course, the first question that is expected after how, is what if we need to add another implementation which is a very likely scenario? For e.g what if we have 2 implementations of the contract, say to return a list of keys in reverse order and the normal one, which one is the container going to choose to bind?

Handling Multiple Exports Within the Container

Well, since the requirements are extended, we need to write another test for the new requirement:

To pass this test, the implementation is trivial:

   1: /// <summary>
   2: ///A test for GetSampleData
   3: ///</summary>
   4: [TestMethod()]
   5: public void GetSampleDataReverseTest()
   6: {
   7:     IDataRetriever target = new DataRetreiverReverse();
   8:
   9:     int expectedCount, count;
  10:     expectedCount = count = 10;
  11:
  12:     IEnumerable<ExampleData> actual = target.GetSampleData(count);
  13:     Assert.AreEqual(expectedCount, actual.Count<ExampleData>());
  14:
  15:     IEnumerator<ExampleData> enumerator = actual.GetEnumerator();
  16:     int expectedKey = count - 1;
  17:     while (enumerator.MoveNext())
  18:     {
  19:         Assert.IsNotNull(enumerator.Current);
  20:         Assert.AreEqual(enumerator.Current.DataKey, expectedKey.ToString());
  21:         expectedKey–;
  22:     }
  23: }

It is obvious that if we don’t change the helper class which does the magic, we won’t get the new implementation. So let’s add it by using AddComponent generic method :

   1: public static void InitializeContainer<T>(T toFillDependency)
   2:     where T:class
   3: {
   4:     CompositionContainer container = new CompositionContainer();
   5:     container.AddComponent<T>(toFillDependency);
   6:     container.AddComponent<DataRetriever>(new DataRetriever());
   7:     container.AddComponent<DataRetreiverReverse>(new DataRetreiverReverse());
   8:     container.Bind();
   9: }

Ok, let’s run the application, and face a very nice error message:

“There was at least one composition issue of severity level ‘error’. Review the Issues collection for detailed information”

“WTF is issues collection?” were the first words out of my mouth unfortunately :) . The exception we get here is a System.ComponentModel.Composition.CompositionException and the “issues” is the Issues property of the exception we are getting. This is a collection of System.ComponentModel.Composition.CompositionIssue object, and their Description field is a string that has the meaningful explanation of what’s happening. In the list I got there was 2 issues that we were already expecting:

  1. “Multiple exports were found for contract name ‘MEFSample.Interfaces.IDataRetriever’. The import for this contract requires a single export only.”
  2. “A failure occurred while trying to satisfy member ‘Retriever’ on type ‘default_aspx’ while trying to import value ‘MEFSample.Interfaces.IDataRetriever’. Please review previous issues for details about the failure.”

Apart from not getting the exceptions in the first go, and ignoring the first message is cryptic, well, this is nice. Theoretically I can see all the errors happened during the build up process, not get stuck in the first one.

Back to the game, now I have 2 implementations in the container, I need a mechanism to choose between two – There is where this System.ComponentModel.Composition.ImportInfoCollection comes into the game. This collection holds a list of ImportInfo objects, which are basically information about the injected members, nothing more. Now, the new property will go as follows:

   1: [Import(typeof(IDataRetriever), IsOptional = false)]
   2: public ImportInfoCollection<IDataRetriever> ResolvedDependencies
   3: {
   4:     get;
   5:     set;
   6: }

When “Bind” is called, this property will be filled automatically instead of the old one. So now I have a list, I am able to decide between the resolved dependencies. Here is my new Retriever property:

   1: public IDataRetriever Retriever
   2: {
   3:    get
   4:    {
   5:        return ResolvedDependencies[0].GetBoundValue();
   6:    }
   7: }

Here I am choosing the first one; I can choose the 2nd one as well since Resolved Dependencies collection will have 2 values.Okay, [0] seems a cumbersome way of selecting the “correct” one, admitted :). Frankly, MEF team included the mechanism in this CTP so we can also specify metadata information along with the injected interfaces, which will help us to be able to decide better what implementation to choose in the run time. But this post got long so I hopefully throw another post to explore what we can do within MEF boundaries.

You can download the sources by clicking here.

kick it on DotNetKicks.com

Share it on: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Blogosphere News
  • e-mail
  • YahooMyWeb
  • DotNetKicks
  • DZone

8 Responses so far »

  1. 1

    Reflective Perspective - Chris Alcock » The Morning Brew #116 said,

    June 17, 2008 @ 6:37 am

    […] Managed Extensibility Framework (MEF) at a Glance - Sidar Ok explores the Managed Extensibility Framework CTP, working through the initial stages of getting things working as intended. […]

  2. 2

    WSDAC#8 - MVP MVC and everything in-between - Service Endpoint said,

    June 25, 2008 @ 3:02 am

    […] Chris Alcock Managed Extensibility Framework (MEF) at a Glance - Sidar Ok explores the Managed Extensibility Framework CTP, working through the initial stages of […]

  3. 3

    Exploring MEF Extensibility Points | Sidar Ok Technical Blog ! said,

    July 5, 2008 @ 2:15 pm

    […] Ok Managed Extensibility Framework (MEF), Design Patterns, Dependency Injection, .NET 3.5 After I had a chance to dance with MEF, I wanted to go a step further and create my own logic to Bind the dependencies, and integrate it […]

  4. 4

    What is this Managed Extensibility Framework thing all about ? | Sidar Ok Technical Blog ! said,

    September 26, 2008 @ 12:39 am

    […] this is not another introduction post. Being one of the earliest adopter of MEF, I started to see a great confusion around what MEF is, […]

  5. 5

    8am and The Managed Extensibility Framework - Casey Charlton - Insane World said,

    September 30, 2008 @ 6:51 am

    […] Microsoft is the Managed Extensibility Framework. I know this is a good thing, because Ayende and Sidar blogged and told me it […]

  6. 6

    MEF « Tales from a Trading Desk said,

    December 24, 2008 @ 8:41 pm

    […] the test of time. “. I suppose the obvious question is, will Unity continue to evolve once MEF is released? Possibly related posts: (automatically generated)CAB: Patterns and Practices LabBids […]

  7. 7

    Gavin said,

    May 19, 2009 @ 1:05 pm

    Hi Sidar, I am glad you find the attribution very intrusive, I have played around with it and think it smells for the following reasons:

    - The composite pattern is clunky, so you have to write setup code for composing the parts in batches which I find silly. Surely you can inject a configuration. Most IoC’s do this. Why do we need compiled code.

    - Why is there no proxy for imports? How do I intercept?

    - What about ordering within the imports? Everything uses collections, you can setup cardinality but nothing so far suggests priority.

    - Why do I have to attribute contructors, surely you could reflect parameters, and based on their types you could resolve the params, just like Castle Windsor does.

    I know the MEF != IoC/DI, but there is alot they could have borrowed, instead of building this clunky, invasive attributed monster!

    I would love you hear your comments on this.

  8. 8

    Sidar Ok said,

    May 20, 2009 @ 10:03 pm

    Hi Gavin,

    This was written for Preview 1, and MEF has done a great progress since then. Per your assertions :

    >> The composite pattern is clunky, so you have to write setup code for composing the parts in batches which I find silly. Surely you can inject a configuration. Most IoC’s do this. Why do we need compiled code.

    I personally favor compiled code, even though I attempted this in the past : http://www.sidarok.com/web/blog/content/2008/07/14/building-a-configuration-binder-for-mef-with-poco-support.html . You can provide your config - based parts with external configuration using Export Providers in the latest drop of MEF. (I guess there is already one in MEF contrib but not quite sure)

    >> Why is there no proxy for imports? How do I intercept?

    Because MEF gives you the composition hook, and you provide your logic to customize container behavior. IMHO this is done best again with Export Providers : http://codebetter.com/blogs/glenn.block/archive/2008/12/25/using-exportprovider-to-customize-container-behavior-part-i.aspx

    >> Why do I have to attribute contructors, surely you could reflect parameters, and based on their types you could resolve the params, just like Castle Windsor does.

    http://codebetter.com/blogs/glenn.block/archive/2009/04/27/poco-mef-and-custom-type-systems-are-you-ready-to-take-the-red-pill.aspx Be sure to read the comments especially..

    >> What about ordering within the imports? Everything uses collections, you can setup cardinality but nothing so far suggests priority.

    There is a MetaData that comes along with them, and you can amend this per your needs.

    >> I know the MEF != IoC/DI, but there is alot they could have borrowed, instead of building this clunky, invasive attributed monster!

    You can do IoC with MEF, or use existing IoC to host your extensions ( http://blogs.msdn.com/nblumhardt/archive/2009/03/16/hosting-mef-extensions-in-an-ioc-container.aspx ) but MEF indeed takes care of a lot more different things as well ( http://www.sidarok.com/web/blog/content/2008/09/26/what-is-this-managed-extensibility-framework-thing-all-about.html )

    Hope this all make sense. Sorry if caused any confusion.

Comment RSS · TrackBack URI

Say your words

You must be logged in to post a comment.