Allright, so I have been pretty quiet if not silent on this blog the last few weeks.
A few articles in the Draft box, but – we’ve all been there – not the time to proof-read it before unleashing it in the wild…
But that one definitely had to go on the fast track, so here it is :
I was working on a project last week where I had to deal with discoverability – in my book, that spells MEF - while still using the principles that are near and dear to my heart, and here, I’m thinking about Dependency Injection (DI) and Inversion of Control (IoC).
Of course, this was in a WPF project, so I wanted to use my beloved Prism.
If I remember correctly, I came across a newsgroup where Glenn The MEF guy Block stated that you should use your favorite IoC container for known dependencies, such as factories, services, and other "built-in" bricks for which you might want to change the implementation one day (let’s say for blendability purposes, switchable alternative business logic or whatever good reason suits you) and MEF for all those dependencies you don’t know / can’t know beforehand.
… And I absolutely agree with Glenn : in the past, I’ve tried to use MEF as an IoC, but don’t even bother trying this at home : you’ll end up overriding GetExports methods in your homebrew custom Catalog to get there… not a good idea.
So, since using MEF as an IoC would require its fair share of arm twisting, why not using the right tools for the right job ? after all, Glenn Block even said so in this two-parts article …
The article looks promising, since it is supposed to answers some question such as : how do I get known dependencies to be injected in the constructors of unknown dependencies : in other words : how do I get two containers (IoC + MEF) to tap into each others to resolve the types they require !
Interesting stuff, can’t wait to read it, you might think…
The only problem is just that now, one year and a half later, Part 2 where the theory would be applied in practice has still not been published.
In the meantime, though, a few happy coders (including Glenn himself ) teamed up to create the MefContrib project on codeplex, but don’t jump around too quickly.. there’s a catch
First Steps with MefContrib
After spending some time across outdated code snippets on blogs and outdated code samples on the official repositories (some samples are still meant for .net 3), I realized that I was missing an assembly reference (thanks Resharper !) so… really, if you’re going to try : do yourself a favor, note that the samples on the documentation page are calling a method called :
Try not going crazy when you don’t find it, it’s just because it’s an extension method… And you can find it in MefContrib.Integration.Unity.UnityContainerExtensions
With that one out of the way, use the UnityBootstrapper as a base class to write your Prism bootstrapper and let’s try to use it.
If you’re lucky, everything will be fine and you can go on living happily ever after.
If you’re not lucky, you’ll get this sweet little exception which will make you go insane
Microsoft.Practices.Prism.Regions.Behaviors.RegionCreationException was unhandled by user code
Message=An exception occurred while creating a region with name ‘MainContent’. The exception was: System.Collections.Generic.KeyNotFoundException: controlType
The annoying thing with that one is that if you had regions before using MefContrib, they were working absolutely fine before you start trying to get MEF and Unity to work together through MefContrib… Don’t believe me ? just go ahead, remove the RegisterCatalog method from the container and the sky is back to being sunny with birds singing in the trees.
The problem is actually absolutely not related to regions, but rather to a set of coincidences involving Prism’s Unity extensions and the order in which MEFContrib resolves the types from Unity and MEF.
Analyzing the culprit
if we pay close attention to the region mappings, we’ll see that the XAML is parsed, regions are found, and they are added to the mappings. The problem is that when the exception is raised, we can see that the mappings list is empty. Analyzing the code shows no place where the mappings get cleared, the only reasonable reason would be that mappings are still in the mappings list, but the mappings isn’t here : calling GetHashCode on the mappings list both when elements are added to the list and before the exception is thrown will confirm that we are not working with the same object !
In order to know why, I just added a constructir to RegionAdapterMappings and set a breakpoint.
Surprise ! the constructor gets called twice : once in the Microsoft.Practices.Prism.Bootstrapper class (in the ConfigureRegionAdapterMappings method) and a second time when the MefDelayedRegionCreationBehavior class is instanciated.
If we have a closer look at its constructor, we’ll see that the type of the injected RegionAdapterMappings object is actually a MefRegionAdapterMappings object.
We are then dealing with two MEF objects which are bridges to expose prism classes through Mef part exports, even though we were using a UnityBootstrapper : There is definitely something wrong here.
First : you most certainly know it if you’re using such advanced MEF scenarios : if an assembly that exports parts is seen by the right catalog, all its parts will be available for import and automatic constructor injections, so the shoer answer to "Why does that happen" is "Because there are MEF parts that are interfering with the other Unity types and that get picked before.
Just remove the Prism.MefExtensions from your project references and your folders and you’re all set, but chances are you still need this dll and you can’t get rid of it that easily. Of course, you can always try to fiddle with catalogs, for instance, the DirectoryCatalog lets you set a patterns to match the filenames to consider in the imports, but there’s only so much you can do that way. Plus, if you want to use some Prism-related MEF attributes, such as ExportModuleAttribute, you’re out of luck with this solution… but at least, now you know you are on the right path.
Let’s take a step back : We use the UnityBootstrapper to register in the IoC the objects that Prism creates behind the scene. That means that Prism creates a RegionAdapterMappings object and makes it available for Unity. The annoying thing here, is that we have seen that when a RegionAdapterMappings is required by a constructor injection, the MEF+Unity type resolution returns a MefRegionAdapterMappings object, that is : a bridge to a RegionAdapterMappings as exposed by MEF when a MefBootstrapper is used instead of a UnityBootstrapper : At some point, a UnityBootstrapper-instanciated object is being ignored only to be replaced by a MefBootstrapper-instanciated counterpart.
What is really disturbing is that if one takes a deep dive in the gitHub repository for MefContrib, he will come across this comment, stating that Unity components always take precedence over MEF. Obviously there something inconsistent here, right ? Well not really : If we look at the base types for DelayedRegionCreationBehavior and RegionAdapterMappings, we will see that they don’t implement any interface. That also means that unity cannot register a type for a specific interface. Instead, it looks like Unity considers there is no need to register anything, since RegionAdapterMappings has a default parameterless constructor and therefore, can be instanciated by itself. It all works well if only Unity is competing for a specific type, but since MEF is also exporting his own version of the type, Unity is overtaken by MEF.
If you stop a minute to think about it, it kind of makes sense, since if we wanted to let Unity resolve any implicitly registered types, MEF would almost never be able to export his objects, even though he would export them explicitly and Unity implicitly.
Fixing it !
There are three approach to fix this :
- Yourself : in your Bootstrapper, when configuring the catalogs, before calling the ConfigureCatalogs from the base class, just register it explicitly, the method in the base class will handle a prior registration properly and use it to add new region mappings.
- Unity : If the guys at Pattern & Practices agree with me, they could register those types explicitly in the base bootstrapper class instead of forcing every developer using MefContrib to do it themselves in their own bootstrapper.
- MefContrib : Maybe they could add a workaround so no changes to Unity would be required, and so the developer using MefContrib would not have to explicitly register all the problematic types. The idea would be to list all those types and register them when the RegisterCatalog extension method is called.
After having done so, it is possible to resolve a type that has been registered in one container and use the automatic constructor injection even for types to inject that have been registered in the other container… and still using regions and still having the MefExtensions dll in the scope of a MEF catalog.
Happy coding !