Skip to content

How MEF is Used in ATF

Gary edited this page Jan 29, 2015 · 5 revisions

Table of Contents

The best way to see how ATF uses MEF is to look at the ATF samples and ATF components. This section examines code in detail to show what's required to use MEF.

Creating a Catalog

In MEF, you need to add components (parts) to a MEF catalog. There are several kinds of MEF catalogs, deriving from the base class ComposablePartCatalog.

Components are what MEF refers to as composable parts. These parts can be put together — composed — into a cohesive whole in the application, where the parts are instantiated and meet each others' import and export contracts, specified by their attributes. This process of putting parts together is called composition.

ATF mainly uses TypeCatalog, which creates a catalog from a collection of types. In ATF, you don't need to instantiate or directly access components to use them in your application. Instead, you simply list them in the TypeCatalog in the application's Main() function, followed by some standard initialization. Each of the parts listed in the catalog is instantiated by MEF, that is, an instance of each class is created during composition. In some cases, more than one instance may be created, depending on the Part Creation Policy.

Here is a typical block of sample code setting up a MEF catalog from the Main() function in Program.cs for the ATF Simple DOM Editor Sample (link to source):

var catalog = new TypeCatalog(
	typeof(SettingsService),                // persistent settings and user preferences dialog
	typeof(StatusService),                  // status bar at bottom of main Form
	typeof(CommandService),                 // handles commands in menus and toolbars
	typeof(ControlHostService),             // docking control host
	...
	typeof(AtfScriptVariables),             // exposes common ATF services as script variables
	typeof(AutomationService)               // provides facilities to run an automated script using the .NET remoting service
	);

TypeCatalog's constructor lists all the types of the components the application uses, one parameter for each type.

Occasionally ATF uses other catalogs besides TypeCatalog. This code, from the StandardInteropParts class, creates a TypeCatalog and returns it as a ComposablePartCatalog type property (link to source):

/// Gets type catalog for all components</summary>
public static ComposablePartCatalog Catalog
{
    get
    {
        return new TypeCatalog(
            typeof(MainWindowAdapter),
            typeof(ContextMenuService),
            typeof(DialogService),
            typeof(ControlHostServiceAdapter)
        );
    }
}

The ATF Simple DOM Editor WPF Sample creates its own TypeCatalog and then uses it and the preceding ComposablePartCatalog from StandardInteropParts (plus a similar catalog from StandardViewModels) to create an AggregateCatalog (link to source):

protected override AggregateCatalog GetCatalog()
{
	var typeCatalog = new TypeCatalog(
		typeof(ControlHostService),             // Docking framework
		typeof(MainWindow),                     // Application's main window
		...  //omitted types
		typeof(BasicPythonService),             // Scripting service for automated tests
		typeof(AtfScriptVariables),             // Exposes common ATF services as script variables
		typeof(AutomationService)               // Provides facilities to run an automated script using the .NET remoting service

		);

	return new AggregateCatalog(typeCatalog, StandardInteropParts.Catalog, StandardViewModels.Catalog);
}

An AggregateCatalog is a catalog that combines elements of ComposablePartCatalog objects and also derives from ComposablePartCatalog.

The ATF Simple DOM Editor WPF Sample uses the AggregateCatalog as a convenience to combine several catalogs. Later on, it uses this catalog similarly to the way other samples use TypeCatalog.

MEF Composition

After creating a catalog of components, samples use the catalog to initiate MEF's composition process.

Here's how ATF Simple DOM Editor Sample does it (link to source):

// Set up the MEF container with these components
var container = new CompositionContainer(catalog);
...
// Give components a chance to clean up.
container.Dispose();

This shows a fairly typical code sequence of how samples use the catalog and kick off the process of components discovering each other.

The following sections describe these MEF operations in detail. Some of the code here, such as creating the main form and toolstrip, is not MEF-related.

Creating a CompositionContainer

A CompositionContainer is a container of components that manages composing the components (parts).

This line creates a CompositionContainer object for the catalog:

// Create the MEF container for the composable parts
CompositionContainer container = new CompositionContainer(catalog);

Add Part for Main Form

A CompositionBatch is a set of parts (components) that can be added to a CompositionContainer. MainForm is also a component class. This next section of code from ATF Simple DOM Editor Sample adds the main form to the CompositionBatch (link to source):

var batch = new CompositionBatch();
...
batch.AddPart(mainForm);

AddPart() creates a composable part from mainForm and adds it to batch.

Compose the Components

Here is the main line of this whole sequence:

container.Compose(batch);

Compose() adds the parts in batch to the parts in the container and composes these parts, creating an object for each part and making sure they meet each other's import and export contracts.

Component Initialization

The comment here from ATF Simple DOM Editor Sample pretty well explains what's going on (link to source):

// 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.
container.InitializeAll();

Invoking InitializeAll() on the container causes the IInitializable.Initialize() method to be executed in every component. InitializeAll() is actually an extension method on CompositionContainer provided by ATF's MefUtil class. Every ATF component should implement IInitializable, that is, implement the IInitializable.Initialize() method. InitializeAll() performs other functions as well, described in IInitializable.

Disposal

When the application terminates, this final statement in Program.cs of ATF Simple DOM Editor Sample releases all resources used by the CompositionContainer (link to source):

// Give components a chance to clean up.
container.Dispose();

MEF Attributes

ATF uses a limited set of MEF attributes in its components. These are mainly used to specify exported and imported items. However, the Export attributes decorating ATF source are not just about exports and imports. For more information, see Initializing Components.

Export

Export attributes nearly always apply to classes. That is, ATF mainly exports its component classes. Some samples export fields.

The Export attribute is used in the component to announce its presence to the world: its contract, which is essentially a string that importers use to find this component. It is nearly always of this form:

[Export(typeof(type)]

where type is the type of the component, usually its class name.

Multiple Export attributes are sometime used, as in this example from StandardFileExitCommand (link to source):

[Export(typeof(StandardFileExitCommand))]
[Export(typeof(IInitializable))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class StandardFileExitCommand : ICommandClient, IInitializable, IPartImportsSatisfiedNotification

ATF often provides a variety of Export attributes on a class, one for each important interface that the class implements, and sometimes for whatever base classes there might be, too. The ATF designers try to anticipate how client code might use ATF components, so ATF provides more Exports than ATF's own code Imports. The attribute [Export(typeof(IInitializable))] has an additional purpose, described in Initializing Components.

[ExportMetadata()] is little used.

Import

[Import] is often used, and it only rarely specifies a type, as in the EditLabelCommand component (link to source):

[Import(typeof(ICommandService))]
private Sce.Atf.Applications.ICommandService m_commandService = null;

Nearly all imports apply to fields and constructors, and to a few properties. Imports are mainly used to connect components to each other. For more information, see Using ATF Components.

Defaults are frequently allowed:

[Import(AllowDefault = true)]

This allows the importer to be set to its type's default value when a matching export could not be found. Defaults are only occasionally not permitted.

For an example of getting a component from an import, see Getting a SettingsService Object.

ImportingConstructor

Note that during composition, the parameter-less constructor is used by default to instantiate the component. To make the composition engine use a different constructor, mark it with the [ImportingConstructor] attribute. ATF often uses this attribute, as in this sample component from the ATF DOM Property Editor Sample (link to source):

[ImportingConstructor]
public Editor(
    IContextRegistry contextRegistry,
    IDocumentRegistry documentRegistry,
    TreeLister treeLister,
    SchemaLoader schemaLoader)

Note also that all the constructor parameters for the constructor decorated with [ImportingConstructor] are in effect decorated as [Import].

Part Creation Policy

This attribute is nearly always Shared:

[PartCreationPolicy(CreationPolicy.Shared)]

Shared means a single instance of the associated component is created by the CompositionContainer during composition and shared by all importers.

A few of these are used:

[PartCreationPolicy(CreationPolicy.Any)]

This means the most appropriate creation policy is used, and defaults to Shared.

AllowRecomposition

A few times AllowRecomposition = true is used, but never AllowRecomposition = false.

Other ComponentModel Classes

ComponentModel provides some other classes that ATF occasionally uses.

ExportProvider Class

The System.ComponentModel.Composition.Hosting.ExportProvider class has methods to get exported components, when it is not convenient to use an [Import] attribute for some reason.

For example, the ATF DOM Property Editor Sample uses one of these methods to get references to a component in its Program.cs Main() function. Such components are often used as parameters in an importing constructor.

The ExportProvider.GetExportedValue<T>() method gets the exported object with the contract name derived from the specified type parameter. In this example from ATF DOM Property Editor Sample, container is an object in the CompositionContainer class, which derives from ExportProvider (link to source):

container.InitializeAll();

var propEditor = container.GetExportedValue<PropertyEditor>();
propEditor.PropertyGrid.PropertySorting = Sce.Atf.Controls.PropertyEditing.PropertySorting.Categorized;

After the imported PropertyEditor component is obtained, its PropertyGrid property is used to set a default for property sorting.

Note that GetExportedValue() is called after all components are initialized.

IPartImportsSatisfiedNotification

ATF implements this interface mainly in its classes that directly support WinForms and WPF, such as classes in the Application Shell Framework, as described in ATF Frameworks.

Topics in this section

Clone this wiki locally