Saturday, November 8, 2008

Inversion of Control pattern and the new Managed Extensibility Framework

MEF (Managed Extensibility Framework) simplifies the creation of extensible applications by offering discovery and composition capabilities to load application extensions. MEF works by discovering extensions via a Catalog and coordinating creation and satisfying dependencies via a CompositionContainer. MEF provides a standard way for the host application to expose itself and consume external extensions. Extensions, by their nature, can be reused amongst different applications. However, an extension could still be implemented in a way that is application-specific. Extensions themselves can depend on one another and MEF will make sure they are wired together in the correct order (another thing you won't have to worry about).

MEF offers a set of discovery approaches for your application to locate and load available extensions.

MEF allows tagging extensions with additional metadata which facilitates rich querying and filtering.

MEF's core is comprised of a catalog and a CompositionContainer. A catalog is responsible for discovering extensions and the container coordinates creation and satisfies dependencies.

MEF's first-class citizen is a ComposablePart. A composable part offers up one or more Exports, and may also depend on one or more externally provided services or Imports. A composable part also manages an instance, which can be an object instance of a given type (it is in the default MEF implementation). MEF, however, is extensible and additional ComposablePart implementations can be provided as long as they adhere to the Import/Export contracts.

Exports and imports each have a Contract. Contracts are the bridge between exports and imports. An export contract can consist of further metadata that can be used to filter on its discovery. For example, it might indicate a specific capability that the export offers.

MEF's container interacts with Catalogs to have access to composable parts. The container itself resolves a part's dependencies and exposes Exports to the outside world. You're free to add composable part instances directly to the container if you wish.

A ComposablePart returned by a catalog will likely be an extension to your application. It might have Imports (dependencies) on components the host application offers, and it's likely to Export others.

The default MEF composable part implementation uses attribute-based metadata to declare exports and imports. This allows MEF to determine which parts, imports, and exports are available completely through discovery.

I will explain the concept by using an example implementation of MEF for creating a logging implementation for a .NET application.

Add a reference to the System.ComponentModel.Composition.dll and System.Reflection.StructuredValues.dll to the newly created application.

Create a new Console application and add the reference to the ComponentModel.Composition assembly.

using System.ComponentModel.Composition;

Create an interface ILogger implementation as follows.

public interface ILogger

{

void Write(string message);

}

Now create a new class MEFApplication that exports the ILogger implementation.

public class MEFApplication

{

[Import]

public ILogger Logger { get; set; }

public void StartSample()

{

//Create a catalog from the executing assembly to look for exports and imports

AttributedAssemblyPartCatalog catalog = new AttributedAssemblyPartCatalog(System.Reflection.Assembly.GetExecutingAssembly());

//Create a container

CompositionContainer container = new CompositionContainer(catalog.CreateResolver());

//Adding the application to the container

container.AddPart(this);

container.Compose();

try

{

throw new ArgumentException("Welcome to MEF");

}

catch (ArgumentException aex)

{

Logger.Write(aex.StackTrace);

}

}

}

The above code created a catalog AttributedAssemblyPartCatalog -- that tells MEF where to look for imports and exports. In this case, we are saying the currently running assembly.

The CompositionContainer is used to wire up all the parts together.

Now you can create a ILogger implementation to Export as

[Export(typeof(ILogger))]

public class ConsoleLogger : ILogger

{

public void Write(string message)

{

Console.WriteLine(message);

}

}

Change the Main method code as

static void Main(string[] args)

{

MEFApplication _mefApplication = new MEFApplication();

_mefApplication.StartSample();

Console.ReadKey();

}

Compile and run the solution .

1 comment:

Anonymous said...

Let me know when I can register POCO types. Having to decorate my types with attributes, base classes, or interfaces is not acceptable. I think I'll stick with Windsor