Convention-Based Programming Model Overview

.NET Framework 4.5

Starting with the .NET Framework 4.5, MEF supports a convention-based programming model in addition to the default attributed programming model. This article explains how to use the convention-based programming model to create MEF applications.

In the Managed Extensibility Framework (MEF), a programming model is a method for defining the set of conceptual objects on which MEF operates. Conceptual objects include parts, imports, and exports. MEF uses these objects but does not specify how they should be represented. Therefore, a wide variety of programming models are possible, including customized programming models.

Note Note

This article assumes that you are familiar with basic MEF concepts such as imports, exports, catalogs, and composition containers. If you are new to MEF, you may want to review the MEF Overview and the Attributed Programming Model Overview first.

For many applications, especially those that are designed to be extensible, the attribute-based programming model provides a good solution for explicitly specifying imports, exports, and their properties. However, it is also possible to use MEF to provide loose coupling between components in a single application. In that context, particularly in a large application, explicitly applying attributes to each type can become cumbersome. The convention-based programming model provides a way for you to establish conventions (rules) to infer the necessary attributes from an object's type or other qualities.

Consider the following type under the attributed programming model:

    [Export()]
    public class HomeController :  Controller
    {
        // Class contents.
    }

The application designer might want to export many such controllers. In the attributed programming model, you'd have to apply the Export attribute to each subclass. By using the convention-based programming model, you can establish a single rule specifying that subclasses of the Controller class should be exported, and any new subclasses added to the application would be affected automatically. In addition, any changes or additions to the rule made necessary by later development would have to be entered only in one place.

The primary tool for using the convention-based programming model is the RegistrationBuilder class. A RegistrationBuilder object lets you create rules that specify which types should be treated as parts, and returns PartBuilder objects that enable you to configure how those types are to be treated.

To create a rule, you can use one of three RegistrationBuilder methods:

  • The ForType method creates a rule that applies to a single type.

  • The ForTypesDerivedFrom method creates a rule that applies to all types that descend from a specified type, but doesn't apply to the type itself, as in the Controller example in the previous section. The specified type can also be an interface, in which case the rule applies to all implementing types.

  • The ForTypesMatching method provides maximum flexibility. It creates a rule that applies to any type that matches the specified predicate.

All three methods also have generic variants that provide PartBuilder<T> objects, which have helper methods that provided simplified syntax to refer to a known type.

For example, to create a rule that applies to all the subclasses of Controller, we would use the following code:

    // RB is the RegistrationBuilder object
    PartBuilder PB = RB.ForTypesDerivedFrom(typeof(Controller));

You can use the PartBuilder object obtained from RegistrationBuilder to configure the imports and exports of the matching part or parts. For example, the following code shows several ways to export the type:

// A simple export.
PB.Export();
// Export as a superclass, in this case Controller.
PB.Export<Controller>();
// Export as a controller with additional metadata.
PB.Export<Controller>(x => x.AddMetadata("Name", "Main"))

To export a particular property or properties of the selected type or types, you can use a predicate to match the properties you need as follows:

// Export all properties whose names contain the word "People"
PB.ExportProperties(pi => pi.Name.Contains("People"));

All configuration is additive, so the same PartBuilder can apply multiple configurations. In addition, the same type can match more than one rule, and if so, all the matching rules will be applied. For example, one rule might specify that all subtypes of Controller should be exported as Controller, another rule might specify that a particular subtype should get additional metadata, and both rules would be applied to that subtype.

Property imports can be configured in the same way as exports. MEF also has the concept of an importing constructor, which is the constructor used by the composition engine to create objects to fill matched imports. By default, the constructor that has the most parameters is used, but you can manually configure a PartBuilder with a particular importing constructor by using the SelectConstructor method:

// Select the constructor with the fewest parameters as the importing constructor.
PB.SelectConstructor(ctors =>ctors.Min(ctor => ctor.GetParameters().Length));

By default, parts created by a RegistrationBuilder object will have the Any creation policy setting. You can override this setting by using a SetCreationPolicyobject, as shown in this example:

// Set the part to use the NonShared creation policy.
PB.SetCreationPolicy(CreationPolicy.NonShared);

Similarly, you can use PartBuilder to add metadata to the part, as follows:

// Add company name information as metadata.
PB.AddMetadata("Name", "MyCompanyName");

Sometimes it is necessary to create one-time exceptions or special cases to a rule. Fortunately, this does not require modifying the code or the rule itself. Instead, any attributes applied to the source code override rules that modify that aspect of the part.

For example, suppose you created a rule to export any subclasses of the Controller class under the type IController. However, you want to export a new subclass of Controller, PrimaryController, as its own type. You can apply the Export attribute to the PrimaryController to override the rule and produce the desired behavior.

Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft