A simple Managed Extensibility Framework (MEF) application using ImportMany

Managed Extensibility Framework (MEF) is a component of .NET Framework 4.0 for creating lightweight, extensible applications. Using this, you can discover and use extensions at run-time with no configuration required. Imagine adding a new functionality to your application by just adding new DLLs to a predefined directory without recompiling! You can read and explore more about MEF at the Codeplex site. So we won't go much into the basics here.

For this simple example, lets consider that we need to process string messages which comes word "FIX:" or "SWIFT:". We will create mappers that can process these message types. Also do note that for that example I am placing all imports and exports in the same project. For true extensibility, do place the exports in external library files.

Make sure you have added references to System.ComponentModel dll and added the namespace System.ComponentModel.Composition & System.ComponentModel.Composition.Hosting. To begin with lets create the contracts which will be the avenue of communication between Imports and Exports. Contracts are nothing but the normal interface.

  public interface IMapper
 {
 String Map(String message);
 }
 
 public interface IMapperMetaData
 {
 String MessageType { get; }
 }

IMapper will be the extensible point for your application. The third party developers who will be extending your application should have reference to this. Apart from this, you can also add metadata information to your export so that you can load the appropriate components only at runtime. 

Next create the program that would compose all the parts (imports and exports are known as parts). I have added comments to the code as appropriate. The customMappers with ImportMany tag will contain all the exported parts which we will be creating later on.

  class MapperClass
 {
 [ImportMany(typeof(IMapper))]
 public IEnumerable<Lazy<IMapper,IMapperMetaData>> customMappers; //Lazy import ensures components are loaded only when required
 
 private CompositionContainer _container;
 
 public MapperClass()
 {
 var catalog = new AggregateCatalog();
 catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly)); //Locations to look for parts. If your export is in a an external dll, you need to use directory catalog as well 
 _container = new CompositionContainer(catalog);
 
 try
 {
 _container.ComposeParts(this); //Compose the imports and exports
 }
 catch (CompositionException ce)
 {
 Console.WriteLine(ce.Message);
 }
 }
 }

Its now time to create our exports. We will create two export parts which will be composed with the import we had defined earlier. You may place these two parts in seperate library, compile it and place it in the parent program executable location. Make sure you add a DirectoryCatalog with the directory to search for parts to the catalogs collection in the program above if you are using seperate DLLs.

  [Export(typeof(IMapper))]
 [ExportMetadata("MessageType", "FIX")]
 class FixMessage : IMapper
 {
 public string Map(string message)
 {
 return "Processed by FIX";
 }
 }
 

Finally its time to test out the program. Create a simple console app as follows:

  static void Main(string[] args)
 {
 MapperClass mc = new MapperClass();
 string s="FIX:2343";
 
 string []types=s.Split(':');
 bool processed = false;
 foreach(var i in mc.customMappers)
 {
 if (i.Metadata.MessageType == types[0])
 {
 processed = true;
 Console.WriteLine(i.Value.Map(s));
 }
 }
 if(!processed)
 Console.WriteLine("Could not process message as no matching mapper was found");
 Console.Read();
 }

The program would work perfectly well. Now change the value of 's' to "SWIFT:3234". The program would say that the mapper was not found. All you need to do is create another export and place it in the same directory.

  [Export(typeof(IMapper))]
 [ExportMetadata("MessageType", "SWIFT")]
 class SwiftMessage : IMapper
 {
 public string Map(string message)
 {
 return "Processed by SWIFT";
 }
 }

Hope I have succeeded in getting you to explore more on MEF, especially for those professionals who work extensively on building extensible applications. Do leave your valuable feedback and comments. Happy coding. :)

 

UPDATE:

To use directory catalog do the following:

catalog.Catalogs.Add(new DirectoryCatalog("."));

Compile FIX and SWIFT classes as seperate library dlls. You now need to place this in the same directory as the Mapper.exe. Do leave a comment if you have any questions.