MEF 101 : Part I

Introduction

After a session I followed in TechEd 2010, and the release of PRISM 4.0, it was time for me to really get into Managed Extensibility Framework. So here is a little article that sums up my experience using it.

The basics

The idea of MEF is to have an easy plugin (called parts) system for .NET.
We can divide MEF in 3 parts :
Export : The part itself.
Import : The host members that are parts.
Composition: The parts discovery/loading mechanism.
A simple example :
First, add reference to System.ComponentModel.Composition
Then, I define the interface the parts must use :

 

interface ICar

  {

  int MaxSpeed { get; }

  string Name { get;}

}

Now create a part using that interface.[/en]

 [Export(typeof(ICar))]
    class Car1 : ICar
    {
        public int MaxSpeed
        {
            get { return 300; }
        }

        public string Name
        {
            get { return "Ferrari"; }
        }
    }
The fact I added the [Export] attribute means the class is a part. I specified the export is of type ICar because it is more flexible to export/import by interface type than by class type (in which case the host application will have to know the class name of the part (here Car1)).
The host application :
class Program
    {
        [Import(typeof(ICar))]
        private ICar _theCar;

        static void Main(string[] args)
        {
            var app = new Program();
            app.Run();
        }

        public void Run()
        {
            var path =
                System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

            var catalog = new DirectoryCatalog(path, "*.exe");
            var container = new CompositionContainer(catalog);

            try
            {
                container.ComposeParts(this);
                Console.WriteLine(string.Format("Car name : {0} Max Speed : {1}",_theCar.Name,_theCar.MaxSpeed));
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadKey();
            }
        }
    }
What I do is :
1) Declare a member of type ICar with the [Import] attribute. That is why I specified in the part the export is of type ICar.
2) Create a DirectoryCatalog. This class will scan de directory for DLL (In my case, as I made a console application, I use an overloaded method and tell it to scan exe files).
3) Add the Catalog to a Container.
4) Load and instantiate the part with the Container.ComposeParts method.
Note that it will only work if there is only one ICar derived part in the catalog, otherwise and exception is thrown. If you need more than one part you can use
 [ImportMany(typeof (ICar))]
 private IEnumerable cars;
Then you will have a ICar parts collection.
As as I said, MEF will instantiate the parts during compositions. If there is a lot of parts, it is not the best option. Wouldn?t it be nice to have a load on demand ? Well…Here it is :

When being Lazy is a virtue

The idea is to use the new .NET 4.0 Lazy class.
[Import(typeof(ICar))]
private Lazy _theCar;

...

Console.WriteLine(string.Format("Car name : {0} Max Speed : {1}",_theCar.Value.Name,_theCar.Value.MaxSpeed));
...
It is quite straightforward : I just created a Lazy member, then I call the parts member with _theCar.Value.XXXX. The part is only instantiated when I use it. Simple magic!

The fun with Metadata : Episode I

The next step is to add some metadata to our part. So we can have a name and a description of the part for instance. All with lazy loading.
The easiest way is just to add some attributes to the part :
[Export(typeof(ICar))]
[ExportMetadata("CarType","Sport")]
public class Car1 : ICar
{
    public int MaxSpeed
    {
        get { return 300; }
    }

    public string Name
    {
        get { return "Ferrari"; }
    }
}
I just added a ExportMetadata attribute.
The import now :
 class Program
    {

        [ImportMany(typeof(ICar))]
        private IEnumerable>> _cars;

        static void Main(string[] args)
        {
            var app = new Program();
            app.Run();
        }

        public void Run()
        {
            var path =
                System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

            var catalog = new DirectoryCatalog(path, "*.exe");
            var container = new CompositionContainer(catalog);

            try
            {
                container.ComposeParts(this);
                foreach (var car in _cars)
                {
                    if ((car.Metadata.ContainsKey("CarType")) && ((string)car.Metadata["CarType"] == "Sport"))
                        Console.WriteLine(string.Format("Car name : {0} Max Speed : {1}", car.Value.Name, car.Value.MaxSpeed));
                }

                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadKey();
            }
        }
    }
I use an overloaded Lazy specific for MEF : Lazy. The metadata is of type . I use a IDictionary so I can use the metadata key easy.
Metadata dictionary is accessed through instance.Metadata.
It is nice, but what about some strongly typed metadata?

The fun with Metadata : Episode II

To use strongly type metadata, we need to do 2 things :
1) Create a new metadata attribute to be used in the export, and a new interface with the same data types for the import. An example :
public enum CarTypeEnum
{
    Sport,
    Family
}

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class CarTypeExportAttribute : ExportAttribute
{
    public CarTypeExportAttribute(Type contractType) : base(contractType)
    {

    }

    public CarTypeEnum CarType { get; set; }
    }
}

public interface ICarType
{
    CarTypeEnum CarType { get; }
{
2) In the host, the import is now :
[ImportMany(typeof(ICar))]
private IEnumerable> _cars;
And to check metadata :
if (car.Metadata.CarType == CarTypeEnum.Sport)
    Console.WriteLine(string.Format("Car name : {0} Max Speed : {1}", car.Value.Name, car.Value.MaxSpeed));
And voila! We have strongly typed metadata.

New parts notification

To make an automatic refresh of the parts when a new dll is added to the parts directory, you can use the FileSystemWatcher class :
...
// Check if any change in directory
_fileSystemWatcher = new FileSystemWatcher(path);
_fileSystemWatcher.Changed += (s, e) => Application.Current.Dispatcher.BeginInvoke(new ThreadStart(() => RefreshOperations()), null);
_fileSystemWatcher.EnableRaisingEvents = true;
_fileSystemWatcher.Filter = "*.dll";

...

private void RefreshOperations()
{
    ...

    // refresh catalog and container
    _catalog.Refresh();
    _container = new CompositionContainer(_catalog);
    ...
}
This is part of the example project I made for this article. It is a WPF application with strongly typed metadata. A good base to start.

The end

There is a lot to say about MEF (like how to manually make the composition,etc…) but this is just an introduction.
In the next part, I will talk about the subtleties of MEF for Silverlight and Phone 7.
Have fun!
The following two tabs change content below.
Olivier

Olivier

Mobile Engineer at Arcana Studio
Freelance developer. Passionate for mobile Development and IoT. Expert in WinRT and Xamarin. MVP Windows Platform Development Nokia Developer Champion