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.
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?
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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