Sex, drugs and sausage rolls

Tech and Life… as seen by Tallmaris (il Gran Maestro)

MEF and Unity in an ASP.NET MVC Application. Part1: MEF

This will be the first of a series of posts mostly about MEF and how it integrates with a proper IoC container, Unity in our examples, to bring extensibility to an MVC Application written in ASP.NET using C# (and the Razor View Engine).

This first post will only deal with what is MEF, how we can use it and why you need it.

MEF

MEF stands for Managed Extensibility Framework and is a framework provided by Microsoft for the run-time discovery of functionality for your application. To put it a bit more simply, it provides means to compose your classes at runtime rather than at compile time, thus allowing you to change the application behaviour by simply changing the deployed assemblies without recompiling your codebase.

As a first example, let’s open Visual Studio and create a new Console Application. Give it a suitable name (which is MEFRoboArena in my case) and let’s get started. Add something like this in the Program.cs and for the time being just ignore the errors:

class Program
{
    static void Main(string[] args)
    {
        var Robot = new Robot("Wall.E");
        Robot.Fire();
        Console.ReadLine();
    }
}

Imports

At this point you can define Robot in the same Assembly but I like to get the functionality of my code into a Core assembly. So, if you want, add a class library to the solution, called MEFRoboArena.Core, and add this class to it:

public class Robot
{
    private readonly string name;
    public string Name { get { return name; } }

    [Import]
    private IWeapon weapon;

    public Robot(string name)
    {
        this.name = name;
    }

    public void Fire()
    {
        Console.WriteLine(String.Format("{0} fired his {1}, dealing {2} damage", name, weapon.Name, weapon.Damage));
    }
}

We will write IWeapon shortly, but first add a reference to “System.ComponentModel.Composition” to the project (and the relevant “using” statement) so that it will recognise the [Import] attribute. This is the first bit of MEF goodness we are going to use and it basically tells MEF that it will have to import the field from somewhere else.

On to the next bit, the Weapons! Weapons will be the pluggable parts of your Core. My suggestion is again to put the Interfaces into a separate Assembly, so that this can be referenced by external plugins without them having access to your Core. Let’s add a class library called MEFRoboArena.Interfaces and create the IWeapon interface:

public interface IWeapon
{
    string Name { get; }
    int Damage { get; }
}

Exports

That was easy… now let’s add a weapon implementation from a Plugin project just to see how MEF combines everything together. Create a project called MEFRoboArena.Plugins, reference the Interfaces project and add a reference to “System.ComponentModel.Composition” here as well (you’ll see why) and add this class:

[Export(typeof(IWeapon))]
public class LaserCannon : IWeapon
{

    public string Name
    {
        get { return "LaserCannon"; }
    }

    public int Damage
    {
        get { return 20; }
    }
}

So now seeing the [Export] attribute should not come as a surprise. It is another attribute provided by MEF to define parts that are exported and as you imagined, the type specification simply states where it can be imported.

The problem is now how to tell MEF to put everything together. If you try to run the application indeed, you will get an exception saying that the “weapon” is null in our Robot. This is where things can get tricky but hopefully everything will become clear in a moment.

Catalogs and Containers

First thing first, let’s add something to our Console App so that it will act as a Bootstrapper (you can create a separate Bootstrapper project of course). Add a reference to the MEF library to the Main project as well (“System.ComponentModel.Composition” as you should know by now) and add the following using statements:

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

then modify the Main as follows:

static void Main(string[] args)
{
    var mefCatalog = new DirectoryCatalog(".");
    var mefContainer = new CompositionContainer(mefCatalog);

    var Robot = new Robot("Wall.E");

    mefContainer.SatisfyImportsOnce(Robot);

    Robot.Fire();
    Console.ReadLine();
}

Let’s see what we did here. The first line creates a MEF Catalog of parts, getting all the DLLs present in the current folder and loading everything decorated with MEF attributes (so both exports and imports). You can read about the various catalogs here.

The second line creates the Composition Container, which helps constructing our objects by matching the export with the imports. With these two objects, we create our Robot instance and feed it to the container extension method “SatisfyImportOnce” which will look for Imports defined and see if it has a matching export that can satisfy it.

Run the program and see what happens… you probably get a “CompositionException” right?

The problem here has nothing to do with MEF but more with the Visual Studio smart compiler, that will not put your Plugins dll in the same folder as the exe, since there is no dependency (which is the good thing we were aiming for). To solve the problem, you can either manually move the dll for the plugins to sit next to the exe, or change the Output Path of your dll in the project properties file to something like “..\MEFRoboArena\bin\Debug\”, or add a post-build event that does the copy for you, whatever you prefer.

Once you moved the plugin dll to the right place, if you run the app again, you should see everything working and the ouput stating that Wall.E fired his Laser Cannon for 20 damage, cool little bot he is :)

Points to take away from this excercise:

1) You have managed to create an Application that will find its dependencies at runtime and not compile time. If this seems to you like no big achievement, think about it and let it settle because it is actually quite important. It means that we may ship this Robot with a Laser Cannon today if we want, but when tomorrow we want a new weapon mounted on, we simply ship the new Weapon Plugin dll alone and arm our robot with a different weapon without having to recompile the whole application. If you look briefly at a dependency diagram you should see something like this:

As you can see, the Plugins.dll is completely decoupled from the Core and the Application itself, with the only link between the two being your interfaces (your Contracts).

2) You may also have noticed that the LaserCannon class is available even though we never constructed it (i.e. we never wrote “new LaserCannon()” anywhere), that is because MEF itself will construct the class in the container to satisfy the Import. This sadly forces us to have a parameterless constructor in the LaserCannon class or MEF would not know how to build it. We will explore constructor injection in MEF and more importantly how to use Unity as well to get around this constraint.

3) Another point to note is that we are actually relying on the Plugin to provide the functionality we need (the weapon), or we get the exception we encountered above. Another exception happens when there are multiple Exports that satisfy a single Import. To solve both problems at once, MEF gives us the [ImportMany] attribute which we will see in the next part.

We will also explore a way to avoid the dependency of the Plugins from the System.ComponentModel.Composition.dll, which is particularly useful if you have other people writing your “weapons” and you don’t want to burden them with flagging all their exports.

Until next time, happy coding to everyone!

, , ,

2 thoughts on “MEF and Unity in an ASP.NET MVC Application. Part1: MEF

  • neutron says:

    This has been the best intro to MEF i could find on the net so far, i tip my hat to you sir, very bare bones and straight to the point. i am a newbie to MEF and and have never done any DI/IoC in my life until today after reading your tutorial

    thank you so much.

    when pray tell will the second part be available?

  • Tallmaris says:

    Thanks for your kind words!
    You are right, I left this linger for too long, the problem being that I moved around in my job and stopped using MEF.
    I’ll see if I can find the time to get down and expand this article with a Part 2.

    Cheers!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>