Wednesday, September 7, 2011

MEF introduction

The Managed Extensibility Framework (MEF) is a new library in .NET that enables greater reuse of applications and components. Using MEF, .NET applications can make the shift from being statically compiled to dynamically composed.

You can download it from here: http://mef.codeplex.com/

AddReference1.JPG

Lets start with what MEF is not another DI (dependency injection), you should think of MEF as plug-ins infrastructure rather then being DI.

MEF is very friendly plug-ins framework which help you to get greater extensibility with less efforts and more consistency. It is very simple decoration module which having supper light dependencies.I hope you find this post useful, I'm having intensions to write future post which will cover the MEF in more details including some advance technique.

MEF presents a simple solution for the runtime extensibility problem. Until now, any application that wanted to support a plugin model (a piece of software which enhances another software application and usually cannot be run independently) needed to create its own infrastructure from scratch. Those plugins would often be application-specific and could not be reused across multiple implementations.
  • 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 additonal metadata which facilitates rich querying and filtering

How does MEF work?

Roughly speaking, 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 (see Parts below). 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 additonal 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.
MEF_Diagram.png

Composable Parts

A Composable Part is a composable unit within MEF. Composable Parts export services that other Composable Parts need, and import services from other Composable Parts. In the MEF programming model, Composable Parts are attributed with the System.ComponentModel.Composition.Import and [System.ComponentModel.Composition.Export] attribute in order to declare their exports and imports. A Composable Part should contain at least one export. Composable Parts are either added to the container explicity or created through the use of catalogs. The default catalogs that MEF ship with identify Composable Parts through the presence of an export attribute.

Contracts

Composable Parts do not directly depend on one another, instead they depend on a contract, which is a string identifier. Every export has a contract, and every import declares the contract it needs. The container uses the contract information to match up imports to exports. If no contract is specified, MEF will implicitly use the fully qualified name of the type as the contract. If a type is passed, it will also use the fully qualified name.

Note: By default a type should be passed for a contract, and not a string. Although contracts can be an arbitrary string this can lead to ambiguity. For example "Sender" might overlap with another implementation of "Sender" in a different library. For this reason if you do need to specify a string constract, it is recommend that contract names should be qualified with a namespace that includes the Company Name for example "Contoso.Exports.Sender".

In the code snippet below, all export contracts are equivalent.
namespace MEFSample 
{
  [Export]
  public class Exporter {...}

  [Export(typeof(Exporter))]
  public class Exporter1 {...}

  [Export("MEFSample.Exporter")]
  public class Exporter2 {...}
}
Namespace MEFSample
    <Export()>
    Public Class Exporter
        ... 
    End Class
    <Export(GetType(Exporter))> 
    Public Class Exporter1
        ... 
    End Class
    <Export("MEFSample.Exporter")>
    Public Class Exporter2
        ... 
    End Class
End Namespace


Contract Assemblies

A common pattern when building extensible applications with MEF is to deploy a contract assembly. A contract assembly is simply an assembly which contains contract types that extenders can use for extending your app. Commonly these will be interfaces, but they may be abstract classes. Additonally contract assemblies will likely contain metadata view interfaces that importers will use, as well as any custom MEF export attributes.
Note: You must specify the specific interface type (IMessageSender) being exported otherwise the type (EmailSender) itself will be exported.


Declaring Imports

Composable Parts declare imports [System.ComponentModel.Composition.ImportAttribute] attribute. Similar to exports, there are several different methods namely through Fields, Properties and Constructor Parameters.

Property Imports

To import a value to a property, decorate the property with the [System.ComponentModel.Composition.ImportAttribute]. For example the snippet below imports an IMessageSender

class Program
  {
    [Import]
    public IMessageSender MessageSender { get; set; }
  }
Class Program
    <Import()>
    Public Property MessageSender() As IMessageSender
End Class


Constructor Parameters

You can also specify imports through constructor parameters. This means that instead of adding properties for each import, you add parameters to a constructor for each import. To use this, follow the following steps.

1. Add a [System.ComponentModel.Composition.ImportingConstructorAttribute] attribute to the constructor that should be used by MEF.
2. Add parameters to the constructor for each import.

For example the code below imports a message sender in the constructor of the Program class.

class Program
  {
    [ImportingConstructor]
    public Program(IMessageSender messageSender) 
    {
       ...
    }
  }
Class Program
    <ImportingConstructor()>
    Public Sub New(ByVal messageSender As IMessageSender) 
      ... 
    End Sub
End Class

Parameter imports

There are several different different ways to define imports on the constructor.

1. Implied import - By default the container will use the type of the parameter to identify the contract. For example in the code below, the IMessageSender contract will be used.

class Program
  {
    [ImportingConstructor]
    public Program(IMessageSender messageSender) 
    {
    }
  }
Class Program
    <ImportingConstructor()>
    Public Sub New(ByVal messageSender As IMessageSender) 
    End Sub
End Class

2. Explicit import - If you want to specify the contract to be imported add an [System.ComponentModel.Composition.ImportAttribute] attribute to the parameter.

Field Imports

MEF also supports importing values directly to fields.

class Program
  {
    [Import]
    private IMessageSender _messageSender;
  }
Class Program
    <Import()>
    Private _messageSender As IMessageSender
End Class

Note: note that importing or exporting private members (fields, properties and methods) while supported in full trust is likely to be problematic on medium/partial trust.

Importing collections

In addition to single imports, you can import collections with the ImportMany attribute. This means that all instances of the specific contract will be imported from the container.

MEF parts can also support recomposition. This means that as new exports become available in the container, collections are automatically updated with the new set. For example below the Notifier class imports a collection of IMessageSender. This means if there are 3 exports of IMessageSender available in the container, they will be pushed in to the Senders property during compositon.

public class Notifier 
 {
    [ImportMany(AllowRecomposition=true)]
    public IEnumerable<IMessageSender> Senders {get; set;}

    public void Notify(string message) 
    {
      foreach(IMessageSender sender in Senders)
      {
        sender.Send(message);
      }
    } 
  }
Public Class Notifier
    <ImportMany(AllowRecomposition:=True)> 
    Public Property Senders() As IEnumerable(Of IMessageSender) 

    Public Sub Notify(ByVal message As String) 
        For Each sender As IMessageSender In Senders
            sender.Send(message) 
        Next sender
    End Sub
End Class
EXAMPLE:
 
using.JPG
 
We implement two classes in our namespace MEFIntro. They are as follows:
 
  1. Program (Listing 1)
  2. SimpeHello (Listing 2)
Dependencies in MEF are provided using attributes. There are two basic attributes.
They are Import and Export. If you look at Program class in Listing 1, 
you can figure out that, its string Property message should be importing 
some string. It doesn’t specify what would be the source of this import. 
Now in Listing 2, SimpleHello exports some string property. 
Some other component might need this property. In our example, this is 
Program class. So it is apparent that both these classes depend on each other. 
Now if we just follow cowboy coding, we would directly instantiate 
MyHello in Program class and use MyHello.Message property to populate 
its own Message property. But, with MEF, we don’t need to do that. 
This is because MEF takes care of injecting the dependencies.
To determine the dependencies between software components, 
catalogs are defined (Run method in Program class). 
Here we have used AssemblyCatalog. 
There may be more than one catalog of same or different types. 
There might be other catalogs too, like DirectoryCatalog, etc. 
After adding these catalogs in CompositionContainer, when we call ComposeParts 
method of the container, all dependencies are resolved. 
As you can see that when we call ComposeParts, 
string property Message in MyHello class (specified with Export attribute) 
is copied to the string property Message in Program class 
(specified with Import attribute).

Listing 1

Listing 2

No comments:

Post a Comment