Saturday, August 22, 2009

Improved MEF + Unity Integration Layer

In my last post about Managed Extensibility Framework and Unity DI container I proposed a solution to make both frameworks work together in tandem, which in turn lets the developer leverage all the features from them at the same time. Now, the improved version is out, and it's a part of the MEFContrib project!

Having learned from developing the previous version, I decided to reimplement the integration layer from the ground up. The API changed very little, though ;)

The "biggest" change since the initial version is that you no longer need to provide boolean parameter during component registration in Unity to say that you want the component to be available to MEF. Now, this is done automatically using Unity container extension. So, the initialization of the layer now is super easy as it requires only single line of code :) Consider this:

// Setup
var unityContainer = new UnityContainer();
var aggregateCatalog = new AggregateCatalog();

// Register catalog and types
unityContainer.RegisterCatalog(aggregateCatalog); // this call initializes the layer
unityContainer.RegisterType<IUnityOnlyComponent, UnityOnlyComponent1>();


The first call to RegisterCatalog method does the initialization - under the hoods, ComposiotionContainer is created, and a link between it and Unity is established. Pretty easy, huh ? :)

Next, all the types are public, so for example, if the standard RegisterCatalog method doesn't fit your needs, you can easily create your own using all the provided types as building blocks. The one concrete use case that comes to my mind is that the current implementation of the RegisterCatalog method does not allow you to attach any additional export providers (your own root CompositionContainer for instance) to the underlying CompositionContainer. In such a case, you can easily create separate method which accepts export providers and initializes the layer. Investigating RegisterCatalog method might be beneficial :)

One neat implication of having all types public is that you can now establish one-way synchronization, being MEF to Unity, in which Unity container knows about MEF parts and can resolve them, and Unity to MEF, in which MEF can pull Unity's components.

Finally, I've added support for closed generics, i.e. you can pull components from Unity and MEF using generics.

The usage of this layer is quite well described on MEFContrib wiki page.

kick it on DotNetKicks.com

8 comments:

joekannapat said...

I tried building a simple MEF-UnityIntegration application to test the functionality and I could not get my project to build. Here is the code:

var unityContainer = new UnityContainer();
var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

// Add composition support for unity
unityContainer.AddNewExtension < CompositionIntegration > ();
unityContainer.Configure <CompositionIntegration>().Catalogs.Add(assemblyCatalog);

var mefComponent = unityContainer.Resolve<IMefComponent>();

I am getting these errors when I build:

The type 'MefContrib.Integration.Unity.CompositionIntegration' cannot be used as type parameter 'TExtension' in the generic type or method 'Microsoft.Practices.Unity.UnityContainerBase.AddNewExtension < TExtension > ()'. There is no implicit reference conversion from 'MefContrib.Integration.Unity.CompositionIntegration' to 'Microsoft.Practices.Unity.UnityContainerExtension'.


Error 2 The type 'Microsoft.Practices.Unity.UnityContainerExtension' is defined in an assembly that is not referenced. You must add a reference to assembly 'Microsoft.Practices.Unity, Version=1.2.0.0, Culture=neutral, PublicKeyToken=null'.


The type 'MefContrib.Integration.Unity.CompositionIntegration' cannot be used as type parameter 'TConfigurator' in the generic type or method 'Microsoft.Practices.Unity.UnityContainerBase.Configure < TConfigurator > ()'. There is no implicit reference conversion from 'MefContrib.Integration.Unity.CompositionIntegration' to 'Microsoft.Practices.Unity.IUnityContainerExtensionConfigurator'.

Simon said...

Hi Piotr,
Nice job on this !
However I'd like to submit 2 bugs that we encountered. For more clarity, I posted it on the CodePlex Issue Tracker on MefContrib :

http://mefcontrib.codeplex.com/WorkItem/View.aspx?WorkItemId=4981

Will you have time to investigate or should we try to fix this by ourself ?

Thanks, Simon

Piotr Włodek said...

@Simon
Thank you for spotting both bugs. I have updated the MEF + Unity integration layer, and even included your tests :) However, the MEF Contrib project is not yet updated to reflect these changes (the official release is coming soon, so stay tuned). You can find the changes in my own MEF Contrib release (unofficial) available at my Code Gallery.

I'm going to elaborate on the changes in the coming post about MEF Contrib 0.8, for now please see test units (I had to modify them a little to support new functionality).

Finally, I would point out that although title of this post suggests this is SL only release, it contains both .NET 3.5 and Silverlight 3 code.

Simon said...

Thanks for the update, that's great.

However, I encountered a new bug. To reproduce it, put the following test case in the my previous test class :

public interface IDependOnCountableUnity
{
ICountableUnityComponent Component { get; set; }
}

[Export(typeof(IDependOnCountableUnity))]
public class DependOnCountableUnity : IDependOnCountableUnity
{
[Import]
public ICountableUnityComponent Component { get; set; }
}

[TestMethod]
public void TestUnityInstanceCountDepScoped()
{
CountableUnityComponent.ResetInstanceCount();
IUnityContainer container = ConfigureUnityThenMef();
var childContainer = container.CreateChildContainer();
childContainer.EnableCompositionIntegration();
var countableDep = childContainer.Resolve();
Assert.AreEqual(1, countableDep.Component.InstanceCount);
}

The dependance is not resolved

Piotr Włodek said...

@Simon
Thank you again for discovering another bug. It has been fixed now and your test has been included in the suite. You can grab the latest source code from http://mefcontrib.codeplex.com

Simon said...

Hey Piotr, once again i'm your fellow bug tracker ;-)

This time it is a problem of multiple instanciation :
When you create a ChildContainer and enable composition on it, you re-register your build strategies, causing objects to be double-instanciated (and triple if you create a new child container from the first one).

I fixed it on my private version, but did not write a UnitTest to validate it, by modifying the EnableCompositionIntegration extension method, and the constructor of CompositionIntegration to put a flag to indicate if BuildupStrategies should be registered.

Hope it helps ;-)

Robert said...

Does anyone know if its possible to use a UnityConfigurationSection in conjunction with this? I'd like to inject Unity components into MEF and I've had some trouble getting this to work:

// doesnt work (types don't appear to get registered with MEF, injection does not work)
UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Containers["containerOne"].Configure(container);

// works (types do get registered with MEF, injection works)
container.RegisterType();

Robert said...

The types I specified in RegisterType appear to have been detected as HTML and dropped, here's that code again, replacing angled brackets with square brackets.

// works container.RegisterType[ICyclopsService, CyclopsService]();