Composition analysis tool (Mefx)
The composition analysis tool (Mefx) is a command-line application that analyzes library (.dll) and application (.exe) files containing Managed Extensibility Framework (MEF) parts. The primary purpose of Mefx is to provide developers a way to diagnose composition failures in their MEF applications without the requirement to add cumbersome tracing code to the application itself. It can also be useful to help understand parts from a library provided by a third party. This topic describes how to use Mefx and provides a reference for its syntax.
Get Mefx
Mefx is available on GitHub at Managed Extensibility Framework. Simply download and unzip the tool.
Basic syntax
Mefx is invoked from the command line in the following format:
mefx [files and directories] [action] [options]
The first set of arguments specify the files and directories from which to load parts for analysis. Specify a file with the /file:
switch, and a directory with the /directory:
switch. You can specify multiple files or directories, as shown in the following example:
mefx /file:MyAddIn.dll /directory:Program\AddIns [action...]
Note
Each .dll or .exe should only be loaded one time. If a file is loaded multiple times, the tool may return incorrect information.
After the list of files and directories, you must specify a command, and any options for that command.
List available parts
Use the /parts
action to list all the parts declared in the files loaded. The result is a simple list of part names.
mefx /file:MyAddIn.dll /parts
MyAddIn.AddIn
MyAddIn.MemberPart
For more information about the parts, use the /verbose
option. This will output more information for all available parts. To get more information about a single part, use the /type
action instead of /parts
.
mefx /file:MyAddIn.dll /type:MyAddIn.AddIn /verbose
[Part] MyAddIn.MemberPart from: AssemblyCatalog (Assembly=" MyAddIn, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
[Export] MyAddIn.MemberPart (ContractName=" MyAddIn.MemberPart")
List Imports and Exports
The /imports
and /exports
actions will list all the imported parts and all the exported parts, respectively. You can also list the parts that import or export a particular type by using the /importers
or /exporters
actions.
mefx /file:MyAddIn.dll /importers:MyAddin.MemberPart
MyAddin.AddIn
You can also apply the /verbose
option to these actions.
Find rejected parts
Once it has loaded the available parts, Mefx uses the MEF composition engine to compose them. Parts that cannot be successfully composed are referred to as rejected. To list all the rejected parts, use the /rejected
action.
You can use the /verbose
option with the /rejected
action to print detailed information about rejected parts. In the following example, the ClassLibrary1
DLL contains the AddIn
part, which imports the MemberPart
and ChainOne
parts. ChainOne
imports ChainTwo
, but ChainTwo
does not exist. This means that ChainOne
is rejected, which causes AddIn
to be rejected.
mefx /file:ClassLibrary1.dll /rejected /verbose
The following shows the complete output of the previous command:
[Part] ClassLibrary1.AddIn from: AssemblyCatalog (Assembly="ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
[Export] ClassLibrary1.AddIn (ContractName="ClassLibrary1.AddIn")
[Import] ClassLibrary1.AddIn.memberPart (ContractName="ClassLibrary1.MemberPart")
[SatisfiedBy] ClassLibrary1.MemberPart (ContractName="ClassLibrary1.MemberPart") from: ClassLibrary1.MemberPart from: AssemblyCatalog (Assembly="ClassLibrar
y1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
[Import] ClassLibrary1.AddIn.chain (ContractName="ClassLibrary1.ChainOne")
[Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No valid exports were found that match the constraint '((exportDefinition.ContractName == "ClassLibrary1.ChainOne") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "ClassLibrary1.ChainOne".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', 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)
[Unsuitable] ClassLibrary1.ChainOne (ContractName="ClassLibrary1.ChainOne")
from: ClassLibrary1.ChainOne from: AssemblyCatalog (Assembly="ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
[Because] PartDefinitionIsRejected, The part providing the export is rejected because of other issues.
[Part] ClassLibrary1.ChainOne from: AssemblyCatalog (Assembly="ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
[Primary Rejection]
[Export] ClassLibrary1.ChainOne (ContractName="ClassLibrary1.ChainOne")
[Import] ClassLibrary1.ChainOne.chain (ContractName="ClassLibrary1.ChainTwo")
[Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No valid exports were found that match the constraint '((exportDefinition.ContractName == "ClassLibrary1.ChainTwo") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "ClassLibrary1.ChainTwo".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', 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)
The interesting information is contained in the [Exception]
and [Unsuitable]
results. The [Exception]
result provides information about why a part was rejected. The [Unsuitable]
result indicates why an otherwise-matching part could not be used to fill an import; in this case, because that part was itself rejected for missing imports.
Analyze primary causes
If several parts are linked in a long dependency chain, a problem involving a part near the bottom may cause the entire chain to be rejected. Diagnosing these problems can be difficult because the root cause of the failure is not always obvious. To help with the problem, you can use the /causes
action, which attempts to find the root cause of any cascading rejection.
Using the /causes
action on the previous example would list only information for ChainOne
, whose unfilled import is the root cause of the rejection of AddIn
. The /causes
action can be used in both normal and /verbose
options.
Note
In most cases, Mefx will be able to diagnose the root cause of a cascading failure. However, in cases where parts are added programmatically to a container, cases involving hierarchical containers, or cases involving custom ExportProvider
implementations, Mefx will not be able to diagnose the cause. In general, the previously described cases should be avoided where possible, as failures are generally difficult to diagnose.
Allow lists
The /whitelist
option enables you to specify a text file that lists parts that are expected to be rejected. Unexpected rejections will then be flagged. This can be useful when you analyze an incomplete library, or a sublibrary that's missing some dependencies. The /whitelist
option can be applied to the /rejected
or /causes
actions.
Consider a file named test.txt that contains the text "ClassLibrary1.ChainOne". If you run the /rejected
action with the /whitelist
option on the previous example, it produces the following output:
mefx /file:ClassLibrary1.dll /rejected /whitelist:test.txt
[Unexpected] ClassLibrary1.AddIn
ClassLibrary1.ChainOne