The Managed Extensibility Framework (or MEF for short) has been around the .NET world for a while but I thought I might go through my foray into using MEF with Delphi Prism.
MEF is a new library in .NET Framework 4 and Silverlight 4 that addresses the problem of easily extending and componentising applications by providing a nifty framework. Adding an easy ability to extend your application, whether it be for the purposes of componentising your application or allowing others to extend your application has always been available to Delphi users in the guise of frameworks like RemObjects Hydra, the TMS Plugin Framework or building your own homebrew solution but MEF is very nice framework.
MEF is incredibly powerful and I’ll only really be scratching the surface and introducing it here.
Imagine an application which runs a sequential series of processes on an imaginary input file, not exactly rocket science but this is only a basic introduction after all.
First I begin by defining an Interface in a separate assembly which all my plugins will adhere to:
IFileAction = public interface method ProcessFile(path: String); property Name: String read; property Version: String read; end;
We then write our first plugin, which we’ll conveniently call the MyHelloPlugin. First create a new assembly and reference the base plugin assembly which contains our IFileAction interface and the System.ComponentModel.Composition assembly. We also need to ensure that we reference these in our uses clause of the unit containing our Hello Plugin class. The My Hello Plugin class looks like this:
[Export(typeof(IFileAction))] MyHelloPlugin = public class(IFileAction) const MYNAME = 'The My Hello Plugin'; MYVERSION = '1.0.1'; public method ProcessFile(path: String); property Name: String read MYNAME; property Version: String read MYVERSION; end;
This should be pretty self explanatory but we have two properties for our name and version and a ProcessFile method which will be called by our host app, providing it with a path for the file that we’re processing. You’ll notice the Export attribute which denotes what we’re exporting. We’re specifying the type of the export but in many cases you may not even need to do this. MEF has a very powerful ExportMetaData system for providing information about what you’re exporting which is also worth reading up on.
Next, of course, we need to actually consume the plugins in our main application. To manage my collection of plugins, I’ll create a small, basic class to hold and manage them:
PluginList = class public [ImportMany(typeof(IFileAction))] property FileActions: IList<IFileAction> read write; constructor; end;
Here I’m using the ImportMany attribute to indicate that I’m expecting to import many IFileAction instances and then providing a list with which to populate (which, as always, you must remember to instantiate before you attempt to fill it!).
Next I’ll load up my Hosting catalog and compose the parts of my application.
var aggregateCat := new AggregateCatalog(); // var catalog := new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly()); // var catalog := new AssemblyCatalog('plugins\MyHelloPlugin.dll'); var dirCatalog := new DirectoryCatalog('plugins'); // Add our Catalogs here, aggregateCat.Catalogs.Add(dirCatalog); var container := new CompositionContainer(aggregateCat); // Create our plugin hosting object var pluginList := new PluginList(); // Compose the parts. container.ComposeParts(pluginList);
You’ll note that I create an AggregateCatalog which isn’t strictly necessary in this case because I’m only adding one ComposablePartCatalog to it but in other uses this would also allow me to separately load single Assemblies or a collection of DirectoryCatalog’s. I then load the catalog into a composition container before creating my aforementioned PluginList class and finally pulling all the parts together. It really is that simple. You can test what has been loaded using our now populated PluginList.FileActions collection, like this:
for each plugin: IFileAction in pluginList.FileActions do begin Console.WriteLine('Loaded ' + plugin.Name + ', version ' + plugin.Version); end;
You can now create a new and entirely different plugin assembly (or many, many more) and providing it conforms to our IFileAction interface and is either in our target directory or loaded manually, it will now appear in our list as if by magic.
Once you’ve begun using MEF, you’ll find out how powerful it can be and how easy it makes building modular applications.
If anyone is interested in me uploading the complete project set for this sample then please let me know in the comments below and I can upload it to github. If anyone else has any excellent samples of MEF usage that they can publish we could even start a sample repository.