This morning Dave came up with a great suggestion for finding MEF composition problems from within Visual Studio.
By registering the composition analyzer as an external tool, the root cause of a composition problem can be located in a flash!
Step 1 - Obtain MEFX.EXE
First, you’ll need to build mefx.exe from the MEF Codeplex Drop (at this time, in the non-Silverlight zip files under /Samples/CompositionDiagnostics).
For demo purposes I’ve placed this (and Microsoft.ComponentModel.Composition.Diagnostics.dll) in C:\Tools.
Step 2 – Register MEFX.EXE as an External Tool
In Visual Studio, select the Tools > External Tools menu item. This will bring up a dialog like the following:
Here you’ll need to click Add and enter the information above. Importantly, select the Use Output Window option.
Note the /causes and /verbose switches. This tells mefx to find parts with missing dependencies that aren’t simply the result of ‘cascading’ failures.
Step 3 – Run Composition Diagnostics
Now that the tooling is set up, finding those composition problems is a breeze!
In my example application, I’m unable to resolve instances of Extensions.Abc:
The exception message tells me that no instances of Abc are available, but I know that it is there in my catalog, so something else must be wrong…
In Solution Explorer, I select the executable project:
Then, under the Tools menu, I click the Debug Composition item that we added in Step 2:
This will analyze all of the assemblies in the output directory of the application, to find the root cause of the composition failure.
NOTE: Currently, only .DLL files are searched, so parts in .EXE files won’t be included in analysis. In a future version of MEFX this will change, but for now, I recommend keeping all parts in .DLL files.
The output of the tool is piped to the Output window:
Let’s break down this output line-by-line.
First, the name of the problematic part is displayed:
[Part] Extensions.Def from: DirectoryCatalog (Path="c:\Workspace\ExampleExtensibleApp\Extensions.dll\bin\Debug\")
The text Primary Rejection indicates that the analysis identified this as a root cause.
We then see a bit more information about the part, and the status of each of its imports. The problematic import is shown here:
[Import] Extensions.Def.Ghi (ContractName="c")
[Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No valid exports were found that match the constraint '(((exportDefinition.ContractName == "c") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "Extensions.Ghi".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity")))) AndAlso ((Not(exportDefinition.Metadata.ContainsKey("System.ComponentModel.Composition.CreationPolicy")) OrElse Any.Equals(exportDefinition.Metadata.get_Item("System.ComponentModel.Composition.CreationPolicy"))) OrElse Shared.Equals(exportDefinition.Metadata.get_Item("System.ComponentModel.Composition.CreationPolicy"))))', invalid exports may have been rejected.
at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableParts, ImportDefinition id) in C:\Users\niblumha\Desktop\Microsoft.ComponentModel.Composition.Diagnostics\CompositionInfo.cs:line 157
There’s a lot of information here, but the important thing to note is that the import is for property Ghi, which imports contract “c”.
We now know which import in the composition is problematic, and can possibly determine our root cause manually from this information.
More interestingly though, the tool provides some suggestions for fixing this error. Here we’re told that there is a part in the system that looks like it could satisfy the import, but has some problems:
[Unsuitable] Extensions.Ghi (ContractName="c") from: Extensions.Ghi from: DirectoryCatalog (Path="c:\Workspace\ExampleExtensibleApp\Extensions.dll\bin\Debug\")
[Because] TypeIdentity, The export is a 'System.Object', but the import requires 'Extensions.Ghi'. These types must match exactly (conversions are not supported.)
[Because] CreationPolicy, The import requires creation policy 'Shared', but the exporting part only supports 'NonShared'.
Diagnostics in MEF are a high priority for us. The declarative nature of MEF composition gives us a lot of interesting opportunities for tooling, so expect to see more work along these lines.
As always, feedback is appreciated!