24 March, 2011

MEF: A simple introduction

Using MEF (Managed Extensibility Framework) can be somewhat of a steep learning experience as a number of new concepts are introduced. However, once these concepts are established it proves quite easy to get your head around it.

But first - why should you even bother learning this pretty new (read: .NET 4.0) framework? Well – you can not miss it; a large number of other frameworks are using MEF as the underlying plumbing system. So, you are kind of forced to learn the concept or at least make an overall acquaintance with it. This was my experience when adopting the Caliburn.Micro framework. In addition - as MEF (System.ComponentModel.Composition) comes as a built-in framework in .NET 4.0, it is easy to just reference in VS 2010 and start using it; no need to download a third-party library. At least that’s the sales pitch coming out of the “evangelists” from the empire in Redmond (or their local apprentices).

So – what to do?
MEF presents the following (new) concepts:

Composable Part (simple, a part):
Any member taking part in the composition presented by MEF

Container:
A runtime entity handling instances of Exporting and Importing members. It does so using a Catalog.

Catalog:
Holds a discoverable list of types/assemblies decorated with [Export] and [Import] attributes.

[Import] (attribute):
An attribute you apply to a property/field on a consuming type.

[Export] (attribute):
An attribute you apply to a class that exports some interface/type

These new concepts are used by MEF to perform the runtime composition in your system.

Solution:
The overall solution is seen below. It contains an exe-file (Console) that references a Contracts (library). Furthermore, 2 implementations exists (SqlLogger and TxtLogger) which reference the Contracts assembly to be able to implement the interface ILogger.

image

The solution is seen below in Visual Studio.

Solution (Visual Studio 2010)

Console:
The console (exe-file) is completely unaware of what’s happening. It lives happily without knowing anything (and that’s actually a good thing). The only reference it knows of is the LoggingFacade.

class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("Starting client...");

var facade = new LoggingFacade();
facade.Write("Hello World"); //just write!

System.Console.WriteLine("Done - press key");
System.Console.Read();
}
}


Contracts:
The contracts assembly is where the meat in this solution is. It contains an interface and the fa├žade that makes use of MEF. Pay attention to the [Import] attribute on the property ConcreteImplementation (note that this property is private). This is what tells MEF that LoggingFacade needs (Imports) a type of ILogger.

/// <summary>
///
A logger abstraction
/// </summary>
public class LoggingFacade
{
/// <summary>
///
Initializes a new instance of the <see cref="LoggingFacade"/> class.
/// </summary>
public LoggingFacade()
{
//create catalog of exporting/importing types from the directory where the exe-file is located
var catalog = new DirectoryCatalog(Environment.CurrentDirectory);

//create composition container (doing the composition)
var container = new CompositionContainer(catalog);

//compose (hook together the types behind the scenes)
container.ComposeParts(this);
}

/// <summary>
///
Gets or sets the concrete logger.
/// This simply states that it consumes an 'ILogger' (of some implementation)
/// </summary>
/// <value>
///
The concrete logger.
/// </value>
[Import]
ILogger ConcreteImplementation { get; set; }

/// <summary>
///
Writes the specified message using what ever implementation is presented (found).
/// </summary>
/// <param name="msg">
The MSG.</param>
public void Write(string msg)
{
if (ConcreteImplementation == null) //it not set - return
return;

ConcreteImplementation.Write(msg); //use implementation.
}
}


SqlLogger:
This implementation is very simple. It simply states that it exports a type of ILogger to the world. Who ever wants to use that are free to do so (which in our case is the CompositionContainer in LoggingFacade).

[Export(typeof(ILogger))]
public class SqlLogger : ILogger
{
public void Write(string msg)
{
Console.WriteLine("I'm a SQL implementation...");
}
}


Finally – running the console will present you with this window:



image



Note: By default, MEF will only accept ONE export of each type. Due to this reason, only the SqlLogger is shown here. If you present more than one export of the same type to the CompositionContainer, you have gain an exception (“more than one implementation found!”).




Technorati Tags: ,

No comments: