Partager via


Managed Extensibility Framework Overview

This blog post is going to explain what MEF is, the benefits of using MEF and then a walk through on how to implement it, followed by a screen shot demo.

What is MEF?

The Managed Extensibility Framework, otherwise known as MEF, is essentially a “.NET Dating Service” . I call it a dating service because it allows assemblies to be loaded dynamically in a hosted application (could be WPF or even a Silverlight application) without having to recompile the host application. It can be as simple as just dropping the new assembly into a directory and the host will dynamically load it up.

Benefits

So what are the benefits of this? You could argue that you could easily just use reflection, but the beauty about MEF is that it’s simple to use and understand. It saves you writing a whole plugin model from scratch, and what’s more is that MEF is transferable between projects where most plugin modules are tied down to the application. It’s the ideal way to extend an application and allow other people who have no idea what the host application does under the hood to go forth and extend. It also has the option to Lazy load extensions as well, thanks to a new type Lazy<T>, where T is the type you want to load. Metadata is supported in MEF. You can attribute your extensions with metadata which the host can query or filter on.

I won’t go through all these but I will go through the basics to give you a taste for it.

How does it work?

image_2_29CFC5A4

Starting from the top, MEF is based on having a catalog, similar to that which you find in CAG (Composite Application Guidance). The catalog’s main role is to help discover MEF-able assemblies. There are various types of catalogs like the DirectoryCatalog, AssemblyCatalog, and AggregateCatalog. A DirectoryCatalog is where you specify the directory you would like to load extensions from. An AssemblyCatalog is where you are discovering Parts within the Assembly. The AggregateCatalog allows you to use a mix of catalogs and group them, so you could use both a DirectoryCatalog and an AssemblyCatalog.

In the middle is the Composition Container, this works out what needs to be composed based on dependencies that are required in the parts. It will then resolve those dependencies so that they are composed prior to their usage.

And finally the Parts. Parts consist of exports and imports attributes on their files which helps the container understand which files are consumers (Importers) and which files are producers (Exporters).

To make a plugin that is MEF-able, which can be shared with the host application, they have to share a contract in common by using an interface. The plugin will implement the interface and decorate the class with an [Export(typeof(Interface))] attribute.

The host application will have a property of the same type as the interface, the property will then be decorated with [Import].

You’ll get a clearer idea of Parts in next section.

Implementation

I have created a demo application to help you get a better understand of how easy it is to create a MEF-able application. The application is a dating agency for the Simpsons; it will give us a better idea about how Homer found Marge. The solution I have consists of 4 projects:

image_4_57BD185C

MEFExample (Our demo host application; e.g. The dating application)

PersonInterface (The shared interface that is the contract been the extensions and host)

People (A bunch of classes representing Simpson characters, each of whom implements the IPerson interface)

Homer (Homers extension is all about him, a simple class that implements the IPerson interface)

 

So to start with I’ll begin with the IPerson interface in the PersonInterface project. This is a very simple interface which consists of properties and an Enum for gender.

 /// <summary>
/// The interface that will be shared with all the MEF-able parts.
/// </summary>
public interface IPerson
{
    string Name { get; }
    int Age { get;  }
    Gender Gender { get; }
    List<string> Likes { get; }
    List<string> Dislikes { get; }
}
public enum Gender
{
    Male,
    Female
}

Now that I have my interface I go about creating my People. So as you can see I have a bunch of classes in the People project, each of which implement my IPerson interface. In the code snippet below, for Marge, notice that I have decorated the class with an export attribute. This attribute is telling the container that it is supporting the use of the type IPerson and anyone who imports IPerson is allowed to use it.

 [Export(typeof(IPerson))]
public class Marge : IPerson

So now that I have a bunch of people who are all implementing IPerson I need something to Import these people to make use of them. I have created a WPF application with a ListBox which is bound to a property called People.

 private ObservableCollection<IPerson> people;

[ImportMany(AllowRecomposition = true)]
public ObservableCollection<IPerson> People
{
    get
    {
        return this.people;
    }
    set
    {
        this.people = value;
        this.OnPropertyChanged("People");
    }
}

Please note that I have used ObservableCollection, it is vital to use this collection if you are going to update anything that is UI based. A strongly typed List won’t do it.

Also notice that I have decorated my property with “ImportMany”. What I am basically asking the container to do is load up all instances in the catalog that implement IPerson. Using just Import will only import a single instance in the catalog. I have also set an attribute parameter of AllowRecomposition. This attribute basically asking MEF to dynamically load MEF-able extension whilst the application is running, without the need to restart the application.

So now I have my Exports and my Imports the last thing to do is setup the catalog and container to finish off.

 private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    this.catalog = new DirectoryCatalog("Members");
    var container = new CompositionContainer(this.catalog);
    container.ComposeParts(this);

    this.DataContext = this;

    DispatcherTimer timer = new DispatcherTimer()
    {
        Interval = TimeSpan.FromSeconds(1),
    };

    timer.Tick += new EventHandler(this.Timer_Tick);
    timer.Start();
}

private void Timer_Tick(object sender, EventArgs e)
{
    this.catalog.Refresh();
}

On the Window load what I am doing is creating a new catalog, in this instance it’s a DirectoryCatalog where I am telling MEF to look in the Members directory.

I then create my CompositionContainer, which if you remember is in charge of resolving the parts, and I give it the constructor argument of the catalog it has to play with. I then call ComposeParts to get it ready to initialize. What this will do is inspect the catalog which would have loaded up all the parts, and then starts loading the dependent parts and ensuring all the imports are now loaded and ready to use.

I finally set my DataContext so that the UI knows where to get the data from and then I start a DispatcherTimer (DispatcherTimer is to do with WPF, not MEF, basically ensures the task runs on the UI thread). The reason I do this is because I want to refresh my catalog every second. Doing this allows recomposition to take place and my imports to be updated. I do this by calling the Refresh method on the catalog.

Demo

So remember, I have not added any project references between the host and the extensions. The only reference they all share is the reference the PersonInterface project, which has the IPerson interface. As you can see the Members directory is empty, and other folders show the People assembly and the Homer assembly.

image_6_57BD185C image_8_57BD185C

As you can see my application is loaded and it’s not really very exciting. So, what I will do close the application and place into the Members folder the Springfield assembly.

image_10_57BD185C image_12_57BD185C

So as you can see my application on startup loaded the Springfield assembly with all its members. For my final trick, I am going to drop the Homer assembly in the Members directory whilst it’s still open; which is a bit difficult to prove in a blog so you’ll have trust me when I say it works.

image_14_57BD185C

So as you can see I now have Homer on the list as well.

Hope this article has been useful, again it’s just a quick insight but hopefully enough to give you a background.

Written by Shen Chauhan