Debugging MEF

Debugging problems in the Managed Extensibility Framework (MEF) can be difficult, since the potential issues are different then those in an ordinary application. This topic presents some methods for diagnosing problems specific to MEF, and suggests some possible causes for those problems.

This topic contains the following sections.

  • Discovering a MEF Problem
  • Examining Available Parts
  • Export/Import Mismatches
  • Discovery Problems
  • Part Rejection

Discovering a MEF Problem

The first step in solving a MEF problem is localizing the problem to the MEF portion of the application. The following table lists the problems are specific to MEF:

Problem

Possible Causes

An ImportCardinalityMismatchException is thrown during composition.

An import could not be filled with a match export because of a missing or rejected part.

-or-

An import expecting a single export found more than one match.

A collection with the ImportMany attribute is missing expected contents.

Expected parts were missing or rejected.

An import with the AllowDefault attribute set to true (AllowDefault=true) is unexpectedly not filled.

An expected match was missing or rejected.

Examining Available Parts

To determine what parts are available to the catalog, you can either examine the Parts property of the catalog in the debugger, iterate through that property in your code, or use the Mefx Composition Analysis Tool. For more information about Mefx, see Composition Analysis Tool (Mefx). In particular, you can use the mefx /importers command to check if more than one part matches a given import. This command will produce an exception if that import is not a collection. If the import is not a collection, change the import to a collection by using the ImportMany attribute or remove one of the parts.

If parts you were expecting to see are not present, those parts were either not discovered or were rejected. For more information, see Discovery Problems or Part Rejection.

If you can see a part, but it is not matching the import you expect it to, some type of mismatch has occurred. For more information, see Export/Import Mismatches below.

Export/Import Mismatches

In order for an export to match a particular import, all of the following conditions must be met. If an expected match does not occur, and you have confirmed that the export is present in the catalog, carefully check these conditions. These conditions also apply when manually constructing a contract to match a particular export. For more information, see the GetExports method.

Property

Conditions For Matching

Contract Name

Must match exactly, and is case-sensitive. In the case of contract names inferred from types, for example when the Export attribute is applied with no parameters, the only possible match is another name inferred from the same type.

Contract Type

Must match exactly. Polymorphic matching is not supported. Contract types must match even if matching contract names are supplied.

Required Metadata

All metadata required by the import through the properties of its metadata view, must be provided by the export through an ExportMetadata attribute or custom metadata attribute. Both the metadata keys (the names of the properties in the metadata view) and the types of the metadata values must match.

Creation Policy

Export and import must not specify different creation policies. For more information, see CreationPolicy.

Discovery Problems

If a part does not appear in the catalog or appear when using Mefx, the part is not being discovered by the catalog. There are a few possible reasons for this failure:

  • The part is an abstract type. Abstract types cannot be used as parts. Either make the type non-abstract, or create a non-abstract subtype.

  • The ImportingConstructor attribute is missing. A part with more than one constructor, or only constructors that accept parameters, must specify a constructor for MEF to use with the ImportingConstructor attribute.

  • The part has the PartNotDiscoverable attribute. This attribute prevents a part from being discovered.

  • The part is an open generic type. Open generic types are not supported by MEF. Either use a closed subclass of the type, or export individual properties.

Additional failures can occur when using a DirectoryCatalog:

  • The part is in an EXE file. The default DirectoryCatalog reads only from DLL files. You can use a DirectoryCatalog to read from other files by creating it with the appropriate search pattern.

  • The part's assembly has a missing reference. Assemblies used must be able to load their references from the search path, usually either from their own directory or from the global assembly cache.

  • The part's assembly targets a different CPU type. MEF will not load assemblies targeting the wrong CPU type.

Part Rejection

The MEF composition engine guarantees that a part will never be created in an invalid state. This is called stable composition. In order to accomplish stable composition, parts with unfilled imports are hidden from the container. These parts are referred to as rejected.

Mefx is particularly useful for diagnosing rejection. If a part is listed in the mefx /parts command, but the part does not appear not in the catalog, the part has been rejected. You can also use the mefx /rejected command to get a list of rejected parts. For more information about Mefx, see Composition Analysis Tool (Mefx).

Parts are rejected when they have an import that cannot be matched by an available export, according to the rules discussed in Export/Import Mismatches. However, it is frequently the case that the part initially observed to be unavailable is rejected because the match for one of its own imports has been rejected. That rejection in turn can be caused by yet another rejection, and so on. This is called cascading rejection. The last rejection in the chain is called the primary rejection. Discovering the primary rejection is the main challenge in debugging this type of problem.

For example, consider a part named ChainOne that has one import, of type ChainTwo. ChainTwo in turn imports ChainThree, and ChainThree imports ChainFour. The following code declares these parts:

    [Export]
    public class ChainOne
    {
        [Import]
        public ChainTwo chain { get; set; }
    }

   [Export]
    public class ChainTwo
    {
        [Import]
        public ChainThree chain { get; set; }
    }

    [Export]
    public class ChainThree
    {
        [Import]
        public ChainFour chain { get; set; }
    }

If these are the only parts loaded into the container, ChainThree will be rejected because ChainFour does not exist. There is no matching export, so its imports cannot be filled. This will cause ChainTwo to be rejected, which will cause ChainOne to be rejected. The rejection of ChainThree is the primary rejection.

The best way to discover the primary rejection is to use the Mefx tool. The mefx /causes command is designed to provide this kind of analysis and guide you to the part that is causing the problem. For more information, see Composition Analysis Tool (Mefx).