Share via



February 2010

Volume 25 Number 03

Managed Extensibility Framework - Building Composable Apps in .NET 4 with the Managed Extensibility Framework

By Glenn Block | February 2010

With the upcoming Microsoft .NET Framework 4, you’ll find an exciting new technology at your doorstep that will greatly simplify your application development. If you’ve struggled with how to design applications that are easier to maintain and extend, keep reading.

The Managed Extensibility Framework (MEF) is a new library shipping in the .NET Framework 4 and in Silverlight 4 that simplifies the design of composable systems that can be extended by third parties after they have been deployed. MEF opens up your applications, allowing new functionality to be incrementally introduced by application developers, framework authors and third-party extenders.

Why We Built It

Several years ago, within Microsoft, a number of groups were working to find solutions to a problem—how to build applications from reusable components that can be discovered, reused and composed dynamically:

  • Visual Studio 2010 was building a new extensible code editor. The editor’s core capabilities, as well as third-party capabilities, were all to be deployed as binaries that would be discovered at runtime. One of the core requirements was to support lazily loading extensions in order to improve startup time and memory consumption.
  • “Oslo” introduced “Intellipad,” a new extensible text editor for working with MEF. In Intellipad, plugins were to be authored in IronPython.
  • Acropolis was delivering a framework for building composite applications. The Acropolis runtime discovered application component “parts” at runtime and provided those parts with services in a loosely coupled manner. Acropolis made heavy use of XAML for component authoring.

This problem was not specific to Microsoft. Customers have been implementing their own custom extensibility solutions for ages. Here was clear opportunity for the platform to step in and provide a more general-purpose solution to help both Microsoft and customers.

Did We Need Something New?

MEF is not by any means the first solution in this problem space. There have been many proposed solutions—a long list of ventures that cross platform boundaries and include efforts like EJB, CORBA, Eclipse’s OSGI implementation and Spring on the Java side. On Microsoft’s platform, there are the Component model and System.Addin within the .NET Framework itself. And there are several open-source solutions, including SharpDevelop’s SODA architecture and Inversion of Control containers like Castle Windsor, Structure Map and patterns & practices’ Unity.

With all the existing approaches, why come up with something new? We realized none of our current solutions were ideal for general third-party extensibility. They were either too heavyweight for general use or required too much effort on the part of either the host or the extension developer. MEF represents the culmination of learning from each of these solutions, and an attempt to address the pain points just mentioned.

Let’s take a look at MEF’s core concepts, illustrated in Figure 1.

image: Core Concepts in the Managed Extensibility Framework

Figure 1 Core Concepts in the Managed Extensibility Framework

Concepts

At the heart of MEF are a few essential concepts:

Composable part (or, simply, part)—A part provides services to other parts and consumes services provided by other parts. Parts in MEF can come from anywhere, from within the application or externally; from an MEF perspective, it makes no difference.

Export—An export is a service that a part provides. When a part provides an export, it is said that the part exports it. For example, a part may export a logger, or in the case of Visual Studio, an editor extension. Parts can provide multiple exports, though most parts provide a single export.

Import—An import is a service that a part consumes. When a part consumes an import, the part imports it. Parts can import single services, such as the logger, or import multiple services, such as an editor extension.

Contracts—A contract is an identifier for an export or an import. An exporter specifies a string contract that it provides, and an importer specifies the contract that it needs. MEF derives contract names from the types that are being exported and imported, so in most cases you don’t have to think about it.

Composition—Parts are composed by MEF, which instantiates them and then matches up exporters to importers.

Programming Models—the Face(s) of MEF

Developers consume MEF through a programming model. A programming model provides a means to declare components as MEF parts. Out of the box, MEF provides an attributed programming model, which will be the main focus of this article. That model is just one of many possible programming models that MEF enables. MEF’s core API is completely agnostic to attributes.

Diving into the Attributed Programming Model

In the attributed programming model, parts (known as attributed parts) are defined with a set of .NET attributes, which live in the System.ComponentModel.Composition namespace. In the sections that follow, I’ll explore building an extensible Windows Presentation Foundation (WPF) sales order management application using this model. This application allows customers to add new, customized views within their environments simply by deploying a binary to the bin folder. I’ll look at how this can be implemented through MEF. I’ll incrementally improve the design as I go, and explain more about MEF’s capabilities and what the attributed programming model provides in the process.

Exporting a Class

The order management application allows plugging in new views. To export something to MEF, you export it by using the Export attribute as shown here:

[Export]
public partial class SalesOrderView : UserControl
{
public SalesOrderView()
  {
InitializeComponent();
  }
}

The above part exports the SalesOrderView contract. By default, the Export attribute uses the concrete type of the member (in this case, the class) as the contract. You can also explicitly specify the contract by passing a parameter to the attribute’s constructor.

Importing Through Properties and Fields

Attributed parts can express the things they need by using the import attribute on a property or field. The application exports a ViewFactory part, which other parts can use to get to views. That ViewFactory imports SalesOrderView using a property import. Importing a property simply means decorating a property with an Import attribute:

[Export]
public class ViewFactory
{
  [Import]
  public SalesOrderView OrderView { get; set; }
}

Importing Through Constructors

Parts can also import through constructors (commonly known as constructor injection) by using the ImportingConstructor attribute as shown below. When using an importing constructor, MEF assumes all parameters are imports, making the import attribute unnecessary:

[Export]
public class ViewFactory
{
  [ImportingConstructor]
  public ViewFactory(SalesOrderView salesOrderView)
{
}
}

In general, importing via constructors rather than properties is a matter of preference, though there are times when it’s appropriate to use property imports, particularly when there are parts that aren’t instantiated by MEF, as in the WPF App example. Recomposition is also not supported on constructor parameters.

Composition

With SalesOrderView and ViewFactory in place, you can now start composition. MEF parts don’t automatically get discovered or created. Instead, you need to write some bootstrapping code that will enact composition. A common place to do this is in your application’s entry point, which in this case is the App class.

To bootstrap MEF involves a few steps:

  • Add imports of the contracts you need the container to create.
  • Create a catalog that MEF uses to discover parts.
  • Create a container that composes instances of parts.
  • Compose by calling the Composeparts method on the container and passing in the instance that has the imports.

As you can see here, I added a ViewFactory import on the App class. Then I created a DirectoryCatalog pointing to the bin folder and created a container that uses the catalog. Finally, I called Composeparts, which caused an App instance to be composed and the ViewFactory import to be satisfied:

public partial class App : Application
{
  [Import]
public ViewFactory ViewFactory { get; set; }
public App()
  {
this.Startup += new StartupEventHandler(App_Startup);
  }
void App_Startup(object sender, StartupEventArgs e)
  {
var catalog = new DirectoryCatalog(@".\");
var container = new CompositionContainer(catalog);
container.Composeparts(this);
  }
}

During composition, the container will create the ViewFactory and satisfy its SalesOrderView import. This will result in SalesOrderView being created. Finally, the Application class will have its ViewFactory import satisfied. In this way, MEF has assembled the entire object graph based on declarative information, rather than manually requiring imperative code to do the assembly.

Exporting Non-MEF Items to MEF Through Properties

When integrating MEF into an existing application, or with other frameworks, you will often find non-MEF related class instances (meaning they are not parts) that you will want to make available to importers. These may be sealed framework types such as System.String; application-wide singletons such as Application.Current; or instances retrieved from a factory, such as a logger instance retrieved from Log4Net.

To support this, MEF allows property exports. To use property exports, you create an intermediary part with a property that is decorated with an export. That property is essentially a factory and executes whatever custom logic is necessary to retrieve the non-MEF value. In the following code sample, you can see that Loggerpart exports a Log4Net logger, allowing other parts such as the App to import it rather than depending on accessing the static accessor method:

public class Loggerpart
{
  [Export]
public ILog Logger
  {
get { return LogManager.GetLogger("Logger"); }
  }
}

Property exports are like Swiss Army knives in their functionality, allowing MEF to play well with others. You will find them extremely useful for integrating MEF into your existing apps and for talking to legacy systems.

Decoupling Implementation with an Interface

Going back to the SalesOrderView example, a tightly coupled relationship has been formed between ViewFactory and SalesOrderView. The factory expects a concrete SalesOrderView that limits extensibility options as well as testability of the factory itself. MEF allows imports to be decoupled from the exporter’s implementation by using an interface as the contract:

public interface ISalesOrderView{}
[Export(typeof(ISalesOrderView))]
public partial class SalesOrderView : UserControl, ISalesOrderView
{
   ...
}
[Export]
public class ViewFactory
{
  [Import]
ISalesOrderView OrderView{ get; set; }
}

In the preceding code, I changed SalesOrderView to implement ISalesOrderView and explicitly export it. I also changed the factory on the importer side to import ISalesOrderView. Notice the importer doesn’t have to specify the type explicitly, as MEF can derive it from the property type, which is ISalesOrderView.

This raises the question of whether ViewFactory should also implement an interface like IViewFactory. This isn’t a requirement, though it may make sense for mocking purposes. In this case I’m not expecting anyone to replace ViewFactory, and it’s designed in a testable fashion, so it’s fine. You can have multiple exports on a part in order to have the part imported under multiple contracts. SalesOrderView, for example, can export both UserControl and ISalesOrderView by having an additional export attribute:

[Export (typeof(ISalesOrderView))]
[Export (typeof(UserControl))]
public partial class SalesOrderView : UserControl, ISalesOrderView
{
   ...
}

Contract Assemblies

As you start to create contracts, you will need a way to deploy those contracts to third parties. A common way to do this is by having a contract assembly that contains interfaces for the contracts that will be implemented by extenders. The contract assembly becomes a form of SDK that the parts will reference. A common pattern is to name the contract assembly as the application name + .Contracts, such as SalesOrderManager.Contracts.

Importing Many Exports of the Same Contract

Currently ViewFactory imports only a single view. Hardcoding a member (property param) for each view works for a very small number of predefined types of views that aren’t changing frequently. However, with such an approach, adding new views requires the factory to be recompiled.

If many types of views are expected, MEF offers a better approach. Instead of using a specific view interface, you can create a generic IView interface that all views export. The factory then imports a collection of all available IViews. To import a collection in the attributed model, use the ImportMany attribute:

[Export]
public class ViewFactory
{
  [ImportMany]
IEnumerable<IView> Views { get; set; }
}
[Export(typeof(IView))]
public partial class SalesOrderView : UserControl, IView
{
}
//in a contract assembly
public interface IView{}

Here you can see that ViewFactory now imports a collection of IView instances rather than a specific view. SalesOrder implements IView and exports it rather than ISalesOrderView. With this refactoring, ViewFactory can now support an open set of views.

MEF also supports importing using concrete collections such as ObservableCollection<T> or List<T>, as well as custom collections that provide a default constructor.

Controlling Part Creation Policy

By default, all part instances in the container are singletons, thus they are shared by any parts that import them within the container. For this reason, all importers of SalesOrderView and ViewFactory will get the same instance. In many cases this is desirable, as it replaces having static members that other components depend on. However, sometimes it’s necessary for each importer to get its own instance, for example, to allow multiple SalesOrderView instances to be viewed on the screen at the same time.

Part creation policy in MEF can be one of three values: CreationPolicy.Shared, CreationPolicy.NonShared or CreationPolicy.Any. To specify creation policy on a part, you decorate it with the partCreationPolicy attribute, as shown here:

[partCreationPolicy(CreationPolicy.NonShared)]
[Export(typeof(ISalesOrderView))]
public partial class SalesOrderView : UserControl, ISalesOrdderView
{
public SalesOrderView()
  {
  }
}

PartCreationPolicy can also be specified on the importer side by setting the RequiredCreationPolicy property on the import.

Distinguishing Exports with Metadata

ViewFactory now works with an open set of views, but I have no way to distinguish one view from another. I could add a member to IView called ViewType, which the view would provide, and then filter against that property. An alternative is to use MEF’s export metadata facilities to annotate the view with its ViewType. Using the metadata provides an additional advantage of allowing the view’s instantiation to be delayed until it’s needed, which can conserve resources and improve performance.

Defining Export Metadata

To define metadata on an export, you use the ExportMetadata attribute. Below, SalesOrderView has been changed to export an IView marker interface as its contract. It then adds additional metadata of “ViewType” so that it can be located among other views that share the same contract:

[ExportMetadata("ViewType", "SalesOrder")]
[Export(typeof(IView)]
public partial class SalesOrderView : UserControl, IView
{
}

ExportMetadata has two parameters, a key that is a string and a value of type object. Using magic strings as in the preceding example can be problematic because this isn’t compile-safe. Instead of a magic string, we can supply a constant for the key and an enum for the value:

[ExportMetadata(ViewMetadata.ViewType, ViewTypes.SalesOrder)]
[Export(typeof(IView)]
public partial class SalesOrderView : UserControl, IView
{
  ...
}
//in a contract assembly
public enum ViewTypes {SalesOrderView}
public class ViewMetadata
{
public const string ViewType = "ViewType";
}

Using the ExportMetadata attribute provides a lot of flexibility, but there are several caveats when using it:

  • Metadata keys aren’t discoverable in the IDE. The part author must know which metadata keys and types are valid for the export.
  • The compiler won’t validate metadata to ensure it’s correct.
  • ExportMetadata adds more noise to the code, hiding the intent.

MEF provides a solution to address the above issues: custom exports.

Custom Export Attributes

MEF allows the creation of custom exports that include their own metadata. Creating a custom export involves creating a derived ExportAttribute that also specifies metadata. We can use custom exports to create an ExportView attribute that includes metadata for ViewType:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class ExportViewAttribute : ExportAttribute {
public ExportViewAttribute()
:base(typeof(IView))
  {}
public ViewTypes ViewType { get; set; }
}

ExportViewAttribute specifies that it exports IView by calling Export’s base constructor. It’s decorated with a MetadataAttribute, which specifies that the attribute provides metadata. This attribute tells MEF to look at all of the public properties and create associated metadata on the export using the property name as the key. In this case, the only metadata is ViewType.

The last important thing to note about the ExportView attribute is that it’s decorated with an AttributeUsage attribute. This specifies that the attribute is valid only on classes and that only a single ExportView attribute can be present.

In general, AllowMultiple should be set to false; if it’s true, the importer will be passed an array of values rather than a single value. AllowMultiple should be left as true when there are multiple exports with different metadata of the same contract on the same member.

Applying the new ExportViewAttribute to the SalesOrderView now results in the following:

[ExportView(ViewType = ViewTypes.SalesOrder)]  
public partial class SalesOrderView : UserControl, IView
{
}

As you can see, custom exports ensure the correct metadata is provided for a particular export. They also reduce noise in the code, are more discoverable through IntelliSense and better express intent through being domain-specific.

Now that metadata has been defined on the view, the ViewFactory can import it.

Importing Lazy Exports and Accessing Metadata

To allow the accessing of metadata, MEF leverages a new API of the .NET Framework 4, System.Lazy<T>. It allows delaying the instantiation of an instance until the value property of the Lazy is accessed. MEF further extends Lazy<T> with Lazy<T,TMetadata> to allow accessing export metadata without instantiating the underlying export.

TMetadata is a metadata view type. A metadata view is an interface that defines read-only properties that correspond to keys in the exported metadata. When the metadata property is accessed, MEF will dynamically implement TMetadata and will set the values based on the provided metadata from the export.

This is how ViewFactory looks when the View property is changed to import using Lazy<T,TMetadata>:

[Export]
public class ViewFactory
{
  [ImportMany]
IEnumerable<Lazy<IView, IViewMetadata>> Views { get; set; }
}
public interface IViewMetadata
{
ViewTypes ViewType {get;}
}

Once a collection of lazy exports with metadata has been imported, you can use LINQ to filter against the set. In the following code snippet, I’ve implemented a GetViews method on ViewFactory to retrieve all views of the specified type. Notice it accesses the Value property in order to manufacture the real view instances only for the views that match the filter:

[Export]
public class ViewFactory
{
  [ImportMany]
IEnumerable<Lazy<IView, IViewMetadata>> Views { get; set; }
public IEnumerable<View> GetViews(ViewTypesviewType) {
return Views.Where(v=>v.Metadata.ViewType.Equals(viewType)).Select(v=>v.Value);
  }
}

With these changes, ViewFactory now discovers all views that are available at the time the factory is composed by MEF. If new implementations appear in the container or catalogs after that initial composition, they won’t be seen by the ViewFactory, as it was already composed. Not only that, but MEF will actually prevent the views from being added to the catalog by throwing a CompositionException—that is, unless recomposition is enabled.

Recomposition

Recomposition is a feature of MEF that allows parts to automatically have their imports updated as new matching exports appear in the system. One scenario where recomposition is useful is for downloading parts from a remote server. SalesOrderManager can be changed so that when it starts up, it initiates a download for several optional views. As the views show up, they will appear in the view factory. To make the ViewFactory recomposable, we set the AllowRecomposition property on the ImportMany attribute of the Views property to true, as shown here:

[Export]
public class ViewFactory
{
[ImportMany(AllowRecomposition=true)]
IEnumerable<Lazy<IView, IViewMetadata>> Views { get; set; }
public IEnumerable<View>GetViews(ViewTypesviewType) {
return Views.Where(v=>v.Metadata.ViewType.Equals(viewType)).Select(v=>v.Value);
  }
}

When recomposition occurs, the Views collection will instantly be replaced with a new collection that contains the updated set of views.

With recomposition enabled, the app can download additional assemblies from the server and add them to the container. You can do this through MEF’s catalogs. MEF offers several catalogs, two of which are recomposable. DirectoryCatalog, which you have already seen, is one that is recomposed by calling its Refresh method. Another recomposable catalog is AggregateCatalog, which is a catalog of catalogs. You add catalogs to it by using the Catalogs collection property, which starts recomposition. The last catalog I’ll use is an AssemblyCatalog, which accepts an assembly upon which it then builds a catalog. Figure 2 shows a sample illustrating how you can use these catalogs together for dynamic download.

Figure 2 Using MEF Catalogs for Dynamic Download

void App_Startup(object sender, StartupEventArgs e)
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(newDirectoryCatalog((@"\.")));
var container = new CompositionContainer(catalog);
container.Composeparts(this);
base.MainWindow = MainWindow;
this.DownloadAssemblies(catalog);
}
private void DownloadAssemblies(AggregateCatalog catalog)
{
//asynchronously downloads assemblies and calls AddAssemblies
}
private void AddAssemblies(Assembly[] assemblies, AggregateCatalog catalog)
{
var assemblyCatalogs = new AggregateCatalog();
foreach(Assembly assembly in assemblies)
assemblyCatalogs.Catalogs.Add(new AssemblyCatalog(assembly));
catalog.Catalogs.Add(assemblyCatalogs);
}

The container in Figure 2 is created with an AggregateCatalog. It then has a DirectoryCatalog added to it in order to grab the local parts in the bin folder. The aggregate catalog is passed to the DownloadAssemblies method, which asynchronously downloads assemblies and then calls AddAssemblies. That method creates a new AggregateCatalog, to which it adds AssemblyCatalogs for each download assembly. AddAssemblies then adds the AggregateCatalog containing the assemblies for the main aggregate. The reason it adds in this fashion is to have recomposition occur in one shot, rather than over and over again, which is what would happen if we added assembly catalogs directly.

When recomposition occurs, the collection is immediately updated. Depending on the collection property type, the result is different. If the property is of type IEnumerable<T>, it’s replaced with a new instance. If it’s a concrete collection that inherits from List<T> or ICollection, then MEF will call Clear and then Add for each item. In either case, it means you will have to consider thread-safety when using Recomposition. Recomposition not only relates to adds, it also relates to removals. If catalogs are removed from the container, those parts will also be removed .

Stable Composition, Rejection and Diagnostics

Sometimes a part may specify an import that is missing, as it isn’t present in the catalog. When this happens, MEF prevents the part missing the dependency—or anything that depends on it—from being discovered. MEF does this in order to stabilize the system and prevent runtime failures that would surely occur if the part were created.

Here, SalesOrderView has been changed to import an ILogger though there’s no logger instance present:

[ExportView(ViewType = ViewTypes.SalesOrder)]  
public partial class SalesOrderView : UserControl, IView
{
[Import]
public ILogger Logger { get; set; }
}

Because there isn’t an ILogger export available, SalesOrderView’s export won’t appear to the container. This won’t throw an exception; instead SalesOrderView will just be ignored. If you check ViewFactory’s Views collection, it will be empty.

Rejection will also happen in cases where there are multiple exports available for a single import. In those cases, the part that imports the single export is rejected:

[ExportView(ViewType = ViewTypes.SalesOrder)]  
public partial class SalesOrderView : UserControl, IView
{
[Import]
public ILogger Logger { get; set; }
}
 [Export(typeof(ILogger))]  
public partial class Logger1 : ILogger
{
}
 [Export(typeof(ILogger))]  
public partial class Logger2 : ILogger
{
}

In the preceding example, SalesOrderView will be rejected because there are multiple ILogger implementations, but a single implementation is imported. MEF does provide facilities for allowing a default export in the presence of multiples. For more on this, see codebetter.com/blogs/glenn.block/archive/2009/05/14/customizing-container-behavior-part-2-of-n-defaults.aspx.

You might ask why MEF doesn’t create SalesOrderView and throw an exception. In an open extensible system, if MEF throws an exception, it would be very difficult for the application to handle it, or to have the context to know what to do, because the part might be missing or the import might be nested very deeply in the composition. Without proper handling, the app would be in an invalid state and unusable. MEF rejects the part, thus ensuring application stability is maintained. For more on stable composition, see: blogs.msdn.com/gblock/archive/2009/08/02/stable-composition-in-mef-preview-6.aspx.

Diagnosing Rejection

Rejection is a very powerful feature, but it can sometimes be difficult to diagnose, especially when the entire dependency graph is rejected. In the first early example, ViewFactory directly imports a SalesOrderView. Let’s say MainWindow imported ViewFactory and SalesOrderView is rejected. Then ViewFactory and MainWindow will also get rejected. You might be scratching your head if you see this occur, as you know that MainWindow and ViewFactory actually are present; the reason for the rejection is a missing dependency.

MEF doesn’t leave you in the dark. To assist with diagnosing this problem, it provides tracing. In the IDE, all rejection messages are traced to the output window, though they can also be traced to any valid trace listener. For example, when the app attempts to import MainWindow, the trace messages in Figure 3 will be outputted.

Figure 3 MEF Trace Messages

System.ComponentModel.Composition Warning: 1 : The ComposablepartDefinition 'Mef_MSDN_Article.SalesOrderView' has been rejected. The composition remains unchanged. The changes were rejected because of the following error(s): The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) No valid exports were found that match the constraint '((exportDefinition.ContractName == "Mef_MSDN_Article.ILogger") AndAlso (exportDefini-tion.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "Mef_MSDN_Article.ILogger".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected.

Resulting in: Cannot set import 'Mef_MSDN_Article.SalesOrderView.Logger (ContractName="Mef_MSDN_Article.ILogger")' on part 'Mef_MSDN_Article.SalesOrderView'.

Element: Mef_MSDN_Article.SalesOrderView.logger (ContractName="Mef_MSDN_Article.ILogger") -->Mef_MSDN_Article.SalesOrderView -->TypeCatalog (Types='Mef_MSDN_Article.MainWindow, Mef_MSDN_Article.SalesOrderView, ...').

The trace output shows the root cause of the problem: SalesOrderView requires an ILogger and one cannot be located. We can then see that rejecting it caused the factory to be rejected, and ultimately the MainWindow.

Inspecting Parts in the Debugger

You can go one step further and actually inspect the available parts in the catalog, which I’ll discuss in the section on hosting. In Figure 4 you can see in the watch window the available parts (in the green circles) as well as the required ILogger import (in the blue circle).

image: Available Parts and Required ILogger Shown in a Watch Window

Figure 4 Available Parts and Required ILogger Shown in a Watch Window

Diagnosing Rejection at the Command Line

One of the goals of MEF was to support static analyzability, thus allowing composition to be analyzed outside of the runtime environment. We don’t yet have such tooling support in Visual Studio, however Nicholas Blumhardt authored MEFX.exe (mef.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=33536), a command-line tool that does the trick. MEFX analyzes assemblies and determines which parts are being rejected and why.

If you run MEFX.exe at the command line, you will see a host of options; you can list specific imports, exports or all parts available. For example, here you can see using MEFX to display the list of parts:

C:\mefx>mefx.exe /dir:C:\SalesOrderManagement\bin\debug /parts 
SalesOrderManagement.SalesOrderView
SalesOrderManagement.ViewFactory
SalesOrderManagement.MainWindow

This is useful for getting a part inventory, but MEFX can also track down rejections, which is our interest here, as shown in Figure 5.

Figure 5 Tracking Down Rejections with MEFX.exe

C:\mefx>mefx.exe /dir:C:\SalesOrderManagement\bin\debug /rejected /verbose 
[part] SalesOrderManagement.SalesOrderView from: DirectoryCatalog (Path="C:\SalesOrderManagement\bin\debug")
  [Primary Rejection]
  [Export] SalesOrderManagement.SalesOrderView (ContractName="SalesOrderManagement.IView")
  [Export] SalesOrderManagement.SalesOrderView (ContractName="SalesOrderManagement.IView")
  [Import] SalesOrderManagement.SalesOrderView.logger (ContractName="SalesOrderManagement.ILogger")
    [Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No valid exports were found that match the constraint '((exportDefinition.ContractName == "SalesOrderManagement.ILogger") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "SalesOrderManagement.ILogger".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected.
at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicCompositionatomicComposition)
at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition)
at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableparts, ImportDefinition id)

Dissecting the output in Figure 5 reveals the root cause of the problem: ILogger can’t be located. As you can see, in large systems with many parts, MEFX is an invaluable tool. For more on MEFX, see blogs.msdn.com/nblumhardt/archive/2009/08/28/analyze-mef-assemblies-from-the-command-line.aspx.

Summarizing, there are several advantages of the attributed model:

  • It provides a universal way for parts to declare their exports and imports.
  • It allows systems to dynamically discover available parts rather than requiring preregistration.
  • It’s statically analyzable, allowing tools like MEFX to determine failures ahead of time.

I’ll now take a quick tour of the architecture and see what it enables. At a high level, MEF’s architecture is broken into layers: Programming Models, Hosting and Primitives.

Programming Models Revisited

The attributed model is simply one implementation of those primitives that uses attributes as the means of discovery. The primitives can represent non-attributed parts or even parts that aren’t statically typed, as in the Dynamic Language Runtime (DLR). In Figure 6 you can see an IronRuby part that exports an IOperation. Notice that it uses IronRuby’s native syntax for declaring a part rather than the attributed model, as attributes aren’t supported in the DLR.

image: A Part Example in IronRuby

Figure 6 A Part Example in IronRuby

MEF does not ship with an IronRuby programming model, though it’s likely we will add dynamic language support in the future.

You can read more about experiments in building a Ruby programming model in this blog series: blogs.msdn.com/nblumhardt/archive/tags/Ruby/default.aspx.

Hosting: Where Composition Happens

Programming models define parts, imports and exports. In order to actually create instances and object graphs, MEF provides hosting APIs that are primarily located in the System.ComponentModel.Composition.Hosting namespace. The hosting layer offers a lot of flexibility, configurability and extensibility. It’s the place where much of the “work” in MEF is done and where discovery in MEF begins. Most folks who are simply authoring parts will never touch this namespace. If you are a hoster, however, you’ll be using them as I did earlier in order to bootstrap composition.

Catalogs provide part definitions (ComposablepartDefinition), which describe the available exports and imports. They are the main unit for discovery in MEF. MEF provides several catalogs in the System.ComponentModel.Composition namespace, some of which you already saw, including DirectoryCatalog, which scans a directory; AssemblyCatalog, which scans an assembly; and TypeCatalog, which scans through a specific set of types. Each of these catalogs is specific to the attributed programming model. The AggregateCatalog, however, is agnostic to programming models. Catalogs inherit from ComposablepartCatalog and are an extensibility point in MEF. Custom catalogs have several uses, from providing a completely new programming model to wrapping and filtering existing catalogs.

Figure 7 shows an example of a filtered catalog, which accepts a predicate to filter against the inner catalog from which parts will be returned.

Figure 7 A Filtered Catalog

public class FilteredCatalog : ComposablepartCatalog, 
{
private readonly composablepartcatalog _inner;
private readonly IQueryable<ComposablepartDefinition> _partsQuery;
public FilteredCatalog(ComposablepartCatalog inner,
Expression<Func<ComposablepartDefinition, bool>> expression)
  {
      _inner = inner;
    _partsQuery = inner.parts.Where(expression);
  }
public override IQueryable<ComposablepartDefinition> parts
  {
get
      {
return _partsQuery;
      }
  }
}

The CompositionContainer composes, meaning it creates parts and satisfies imports of those parts. In satisfying the imports, it will grab from the pool of available exports. If those exports also have imports, the container will satisfy them first. In this way, the container assembles entire object graphs on demand. The primary source for the pool of exports is a catalog, but the container can also have existing part instances directly added to it and composed. It’s customary to manually add the entry point class to the container, combined with parts pulled from the catalog, though in most cases parts will come from the catalog.

Containers can also be nested in a hierarchy in order to support scoping scenarios. Child containers by default will query the parent, but they can also provide their own catalogs of child parts, which will be created within the child container:

var catalog = new DirectoryCatalog(@".\");
var childCatalog = new DirectoryCatalog(@".\Child\";
var rootContainer = new CompositionContainer(rootCatalog));
var childContainer = new CompositionContainer(childCatalog, 
rootContainer);

In the preceding code, childContainer is arranged as a child of rootContainer. Both rootContainer and childContainer provide their own catalogs. For more on using the container to host MEF within your applications, see https://codebetter.com/glennblock/2009/11/29/mef-has-landed-in-silverlight-4-we-come-in-the-name-of-extensibility/.

The Primitives: Where Parts and Programming Models Are Born

The primitives located at System.ComponentModel.Composition.Primitives are the lowest level in MEF. They are the quantum universe of MEF, so to speak, and its über extensibility point. Up until now, I’ve covered the attributed programming model. MEF’s container, however, isn’t at all bound to attributes; instead, it’s bound to the primitives. The primitives define an abstract representation of parts, which includes definitions such as ComposablepartDefinition, ImportDefinition and ExportDefinition, as well as Composablepart and Export, which represent actual instances.

Exploring the primitives is a journey of its own, and one I’ll likely take in a future article. For now, you can find out more about it at blogs.msdn.com/dsplaisted/archive/2009/06/08/a-crash-course-on-the-mef-primitives.aspx.

MEF in Silverlight 4—and Beyond

MEF also ships as part of Silverlight 4. Everything I discussed here is relevant to developing extensible Rich Internet Applications. In Silverlight, we’ve gone even further and introduced additional APIs to ease the experience of building apps on MEF. These enhancements will eventually be rolled into the .NET Framework.

You can find out more about MEF in Silverlight 4 in this post: https://codebetter.com/glennblock/2009/11/29/mef-has-landed-in-silverlight-4-we-come-in-the-name-of-extensibility/.

I’ve just scratched the surface of what you can do with MEF. It’s a powerful, robust and flexible tool that you can add to your arsenal to help open up your applications to a whole new world of possibilities. I look forward to seeing what you do with it!        


Glenn Blockis a PM for the new Managed Extensibility Framework (MEF) in the .NET Framework 4. Before MEF he was a product planner in patterns & practices responsible for Prism as well as other client guidance. Block is a geek at heart and spends a good portion of his time spreading that geekdom through conferences and groups such as ALT.NET. Read his blog at https://codebetter.com/glennblock/.

Thanks to the following technical experts for reviewing this article: Ward Bell, Nicholas Blumhardt, Krzysztof Cwalina, Andreas Håkansson, Krzysztof Kozmic, Phil Langeberg, Amanda Launcher, Jesse Liberty, Roger Pence, Clemens Szypierski, Mike Taulty, Mircea Trofin and Hamilton Verissimo