Exploring MEF Extensibility Points

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 to the existing composition system. I have to say that, oh god, it was NOT as easy that as I expected. Current nature of the APIs  heavily depend  on counter intuitive usage. Again, I am having a difficult time to save my critics as a separate post as you can see :)

There are currently 2 main extensibility points in MEF. Custom Binders and Value Resolvers.

Custom Binders

The current nature of the Composition Container is, basically, Container is the god. It needs to have everything to bind, if you want to add a type binding logic, you are welcome. But first, you need to add your type to the container. Well, that’s a fair game, since our logic is Binding(wiring really) logic - not a type or instance management one. This means that we have to write the following as usual:

   1: CompositionContainer container = new CompositionContainer();
   2: container.AddComponent<Consumer>(consumer);
   3: container.AddComponent<Provider>(provider);
   4: container.Bind();

Where Provider and Consumer are just defined with Export & Import model as MEF needs them :

   1: public class Consumer
   2: {
   3:     [Import(typeof(IProvider))]
   4:     public IProvider Provider
   5:     {
   6:         get;
   7:         set;
   8:     }
   9:
  10:     public Consumer()
  11:     {
  12:
  13:     }
  14:
  15:     public int GetAnInteger()
  16:     {
  17:         return this.Provider.GetAnInteger();
  18:     }
  19:
  20: }

And hence is the provider:

   1: [Export(typeof(IProvider))]
   2: public class FavoredProvider : IProvider
   3: {
   4:     #region IProvider Members
   5:
   6:     public int GetAnInteger()
   7:     {
   8:         return 5;
   9:     }
  10:
  11:     #endregion
  12: }

And this makes the following test to pass, which just expects the hard coded “5″ value to be returned:

   1: /// <summary>
   2: ///A test for GetAnInteger
   3: ///</summary>
   4: [TestMethod()]
   5: public void GetAnIntegerTest()
   6: {
   7:        Consumer target = new Consumer();
   8:        CompositionHelper.InitializeContainerForConsumer(target); // this is where container configuration goes
   9:        int expected = 5;
  10:        int actual;
  11:        actual = target.GetAnInteger();
  12:        Assert.AreEqual(expected, actual);
  13: }}

Now, let’s come up with a hypothetical scenario : We need the same instance for every consumer (singleton). How hypothetical that is for a DI container! So  while configuring I would expect to write something close to the following, and still expect test to pass! (Note that you can use IsSingleton for the contract, but the one and only life time controlling mechanism isn’t singleton is it? ;) )

   1: CompositionContainer container = new CompositionContainer();
   2: var myVeryCustomBinder = new SampleBinder();
   3: container.AddComponent<Consumer>(consumer);
   4: // not adding the provider exlusively, expecting the binder to handle
   5: container.AddBinder(myVeryCustomBinder);
   6: container.Bind();

Q: So we didn’t add the provider to the container, haven’t you just said that this is a deal breaker ?

A: Yes it is, you are right. My test now breaks. But I can make that up, in my custom binder. Gimme a break.

To write a custom binder I need to inherit from the abstract class ComponentBinder . In ComponentBinder, there are several virtual methods that are waiting to be overridden depending on my logic:

  1. public override CompositionResult Export() : This is the place that we’ll add the container the results of our resolve operations. The return value also indicates whether the operation is succeeded or not.
  2. public override CompositionResult Import(IEnumerable<string> changedValueNames) : And this is the place that we will retrieve the demanding objects for the services that are exported.
  3. public override CompositionResult BindCompleted() : This kind of acts as an event, (why is it not one?) and will be called when the binding is completed. Here you can do your sanity checks after your bind operations needed for your binder,
  4. public override IEnumerable<string> ExportNames ; These are the “names” for the types. This can be the name specified in the Export() declaration in the attribute or a default one.
  5. public override IEnumerable<string> ImportNames : Same goes with imports, but instead import process.

So here would be my Custom Binder:

   1: public class SampleBinder : ComponentBinder
   2: {
   3:     private FavoredProvider ProviderToInject
   4:     {
   5:         get;
   6:         set;
   7:     }
   8:
   9:     private static readonly object syncRoot = new object();
  10:
  11:     public SampleBinder()
  12:     {
  13:         if (ProviderToInject == null)
  14:         {
  15:             lock (syncRoot)
  16:             {
  17:                 ProviderToInject = new FavoredProvider();
  18:             }
  19:         }
  20:     }
  21:
  22:     public override CompositionResult Export()
  23:     {
  24:
  25:         this.AddValueToContainer(CompositionServices.GetContractName(typeof(IProvider)), this.ProviderToInject, “Provider”);
  26:         return CompositionResult.SucceededResult; ;
  27:     }
  28:
  29:     public override CompositionResult Import(System.Collections.Generic.IEnumerable<string> changedValueNames)
  30:     {
  31:         return base.Import(changedValueNames);
  32:     }
  33:
  34:     public override CompositionResult BindCompleted()
  35:     {
  36:         return base.BindCompleted();
  37:     }
  38:
  39:     public override IEnumerable<string> ExportNames
  40:     {
  41:         get
  42:         {
  43:             return base.ExportNames;
  44:         }
  45:     }
  46:
  47:     public override IEnumerable<string> ImportNames
  48:     {
  49:         get
  50:         {
  51:             return base.ImportNames;
  52:         }
  53:     }
  54:
  55: }

As you see, we are not doing anything on import. For any import of I provider type, we Are providing  the Favored Provider.  Also notice the badly named utility class CompositionServices which currently only has 1 method, and it is a useful one : GetContractName. Without this, I’d have to hardcode it or get it with a drop of reflection magic.

Adding this Binder Makes my test pass, and I am happy.

Value Resolvers

This is an enabled model in this CTP, but the Usage of it is not enabled (at least I couldnt find out a way, since the Resolver value in the container does not have a setter - any feedback on this appreciated(1)) . Value Resolver is the part to provide the types, and probably in a sane implementation life time logic will go here. It is an abstract class and 2 abstract methods are waiting to be overriden, below is a sample Value Resolver:

   1: public class CustomValueResolver : ValueResolver
   2: {
   3:     public override CompositionResult<IImportInfo>
   4: TryResolveToValue(string name, IEnumerable<string> requiredMetadata)
   5:     {
   6:         // do your resolve, and send it back   
   7:         ImportInfo<FavoredProvider> resolvedValue =
   8:             new ImportInfo<FavoredProvider>(null);
   9:         return new CompositionResult<IImportInfo>(true,
  10:             Enumerable.Empty<CompositionIssue>(), resolvedValue);
  11:     }
  12:
  13:     public override CompositionResult<ImportInfoCollection>
  14: TryResolveToValues(string name, IEnumerable<string> requiredMetadata)
  15:     {
  16:     // do the same if you have more than one service to provide to one consumer
  17:         ImportInfo<FavoredProvider> resolvedValue =
  18:             new ImportInfo<FavoredProvider>(null);
  19:
  20:         return new CompositionResult<IImportInfo>(true,
  21:             Enumerable.Empty<CompositionIssue>(), resolvedValue);
  22:     }
  23: }

In the future hopefully we will be able to create our own component catalogs and associate resolvers to them, like the way we can’t to now to both Container and to AssemblyComponentCatalog.(2)

Hope this article gave a deeper hint on what’s going on MEF side of things. Any comments on this are welcome as always.

 (1): Jason pointed out that there is an overload of CompositionContainer, that takes ValueResolver as a parameter. doh! (see comments)

(2): Still couldnt find a way of doing the same to Component Catalog.

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

6 Responses so far »

  1. 1

    Jason Olson said,

    July 4, 2008 @ 6:48 pm

    You actually can use customer resolvers. There is an overload to the CompositionContainer constructor that accepts a ValueResolver parameter.

    With this overload, you can have a custom Resolver that itself will going through and all the custom Binders (so a consumer of your custom classes never need to add binders directly).

    I’ll have a sample going up on my blog today that shows how to use these extension points to build a fluent interface for registration/retrieval of dependencies, without having to use attributes whatsoever.

  2. 2

    Sidar Ok said,

    July 4, 2008 @ 10:04 pm

    Hi Jason, thanks for pointing that out. Looking forward to your post about it.

  3. 3

    Jason Olson said,

    July 4, 2008 @ 10:08 pm

    Hello again Sidar. As mentioned in our private correspondence, my post on creating a Fluent Interface for MEF is now up on my blog: http://www.managed-world.com/2008/07/04/BuildingAFluentInterfaceForMEF.aspx.

    Enjoy!

  4. 4

    Krzysztof Cwalina said,

    July 7, 2008 @ 9:51 pm

    Sidar,

    This is a great post! Really nice you got so far with the very limited documentation that we shipped in the first CTP. It will get better overtime. :-)

    BTW, we are now in the middle of a milestone where we invested very heavily in the MEF extensibility model (which we call “primitives”). Once we ship an update to the CTP, I would be really interested in your feedback on how you compare the CTP #1 extensibility API to the new design.

  5. 5

    Sidar Ok said,

    July 7, 2008 @ 11:19 pm

    Hi Krysztof, thanks and it is a relief to hear that the documentation will get better :)

    I will be constantly watching the deliveries, thanks again!

  6. 6

    Building a Configuration Binder for MEF with POCO Support | Sidar Ok Technical Blog ! said,

    July 14, 2008 @ 2:34 am

    […] About Sidar Sidar Ok Technical Blog ! « Exploring MEF Extensibility Points […]

Comment RSS · TrackBack URI

Say your words

You must be logged in to post a comment.