Skip to content

Initializing Components

Gary edited this page Aug 27, 2014 · 1 revision

Table of Contents

If a component requires initialization, it should implement and export the IInitializable interface. IInitializable and the InitializeAll() CompositionContainer extension method are used to ensure that all components requiring initialization are created and initialized.

IInitializable

The IInitializable interface contains the method Initialize().

As previously mentioned in Component Initialization, the CompositionContainer extension method InitializeAll() initializes components by calling their IInitializable.Initialize() method. This performs initialization on the component that can't be put in the component's constructor. Many ATF components require such initialization.

A component must have been created for it to be initialized. However, MEF creates components lazily, that is, on an as needed basis. Using InitializeAll() creates all the components so they can be initialized.

In ATF, any component that requires initialization implements IInitializable and also decorates its class with this export:

[Export(typeof(IInitializable))]

For a few components, this is the only export decorator. Obviously, this decorator's purpose is not to export the IInitializable interface, but is ATF's convention to mark components that need initialization. The next section explains the details of how initialization works.

MefUtil

The MefUtil class implements the InitializeAll() CompositionContainer extension method. Examining this implementation reveals how the IInitializable interface is used in ATF with MEF to initialize components that need it.

public static void InitializeAll(this CompositionContainer container)
{
    if (container == null)
        throw new ArgumentNullException("container");

    // ImportDefinition
    // - constraint: An expression that contains a Func<T, TResult> object that defines the conditions
    //      an Export must match to satisfy the ImportDefinition.
    // - contractName: The contract name.
    // - cardinality: One of the enumeration values that indicates the cardinality of the Export objects
    //      required by the ImportDefinition.
    // - ImportCardinality.ZeroOrMore: Zero or more Export objects are required by the ImportDefinition.
    // - isRecomposable: true to specify that the ImportDefinition can be satisfied multiple times
    //      throughout the lifetime of a ComposablePart object; otherwise, false.
    // - isPrerequisite: true to specify that the ImportDefinition must be satisfied before a
    //      ComposablePart can start producing exported objects; otherwise, false.
    var importDef = new ImportDefinition(contraint => true, string.Empty, ImportCardinality.ZeroOrMore, true, true);

    try
    {
        // Create everything. This ensures that all objects are constructed before IInitializable
        //  is used. Note that the order of construction is not deterministic. We have seen the same
        //  code result in different orderings on different computers.
        foreach (Export export in container.GetExports(importDef))
        {
            // Triggers creation of object (otherwise lazy).
            // Also, IPartImportsSatisfiedNotification.OnImportsSatisfied() will be called here.
            object tmp = export.Value;
        }

        // Initialize components that require it. Initialization often can't be done in the constructor,
        //  or even after imports have been satisfied by MEF, since we allow circular dependencies between
        //  components, via the System.Lazy class. IInitializable allows components to defer some operations
        //  until all MEF composition has been completed.
        foreach (IInitializable initializable in container.GetExportedValues<IInitializable>())
            initializable.Initialize();
    }
    catch (CompositionException ex)
    {
        foreach (var error in ex.Errors)
            Outputs.WriteLine(OutputMessageType.Error, "MEF CompositionException: {0}", error.Description);

        throw;
    }
}

Analyzing this in detail, this line creates an ImportDefinition object:

var importDef = new ImportDefinition(contraint => true, string.Empty, ImportCardinality.ZeroOrMore, true, true);

The first parameter returns true, so any Export satisfies this ImportDefinition, which is then used in the following loop:

foreach (Export export in container.GetExports(importDef))
{
    // Triggers creation of object (otherwise lazy).
    // Also, IPartImportsSatisfiedNotification.OnImportsSatisfied() will be called here.
    object tmp = export.Value;
}

GetExports() gets all exports that match the conditions specified in importDef, but this import imposed no conditions, so GetExports() obtains all exports in the container. That is, every component in the MEF container that has an \Export\ attribute is obtained. The next line forces that component to be created. MEF creates objects lazily, so unless a component is imported, it might not be created otherwise.

Now that all components are created, the next lines perform any necessary initialization:

// Initialize components that require it. Initialization often can't be done in the constructor,
//  or even after imports have been satisfied by MEF, since we allow circular dependencies between
//  components, via the System.Lazy class. IInitializable allows components to defer some operations
//  until all MEF composition has been completed.
foreach (IInitializable initializable in container.GetExportedValues<IInitializable>())
    initializable.Initialize();

GetExportedValues<IInitializable>() gets all the exported objects with the contract name derived from the specified type parameter, that is, all components that export IInitializable. It then calls Initialize() for each of these components.

In summary, if a component requires initialization, it should implement and export IInitializable. Calling InitializeAll() in the Main() function that sets up the MEF catalog ensures that each component is initialized.

Topics in this section

Clone this wiki locally