Simple Introduction to Extensible Applications with the Managed Extensions Framework

Recently my team has been working on the Managed Extensions Framework (MEF)... I have gotten a chance to explain the concept to folks and I think I have discovered a way to talk about MEF that folks can easily get.  So I thought I'd spend a little time walking through a *very* simple MEF example as a way to introduce folks to the power of extensible applications in general, and MEF in particular. 

BTW, you can download the current MEF CTP  and the final working sample.

Background

Let's start with the most simple example: Hello World! 

    1: using System;
    2:  
    3: class Program
    4: {
    5:     public void Run()
    6:     {
    7:         Console.WriteLine("Hello World!");
    8:         Console.ReadKey();
    9:     }
   10:     static void Main(string[] args)
   11:     {
   12:         Program p = new Program();
   13:         p.Run();
   14:     }
   15: }

Now, you might now always want to print the same string, so let's refactor slightly to pull the string out.. 

    1: public string Message { get; set; }
    2:  
    3: public void Run()
    4: {
    5:     Console.WriteLine(Message);
    6:     Console.ReadKey();
    7: }

This looks nice, now we need to add the message... well, the actual text is a separate concern, as such it should be in a different class.    Such as:

    1: public class SimpleHello 
    2: {
    3:     public string Message
    4:     {
    5:         get
    6:         {
    7:             return "hello world!!";
    8:         }
    9:     }
   10: }

Now we simply need wire these up:

    1: public void Run()
    2: {
    3:     SimpleHello hello = new SimpleHello();
    4:     Message = hello.Message;
    5:  
    6:     Console.WriteLine(Message);
    7:     Console.ReadKey();
    8: }

This works, but something looks odd about line 3 and 4... We have introduced tight coupling back.. What we really want to do is externalize lines 3 and 4, so they can be controlled without effecting the rest of the logic of the program. 

 

Enter MEF

Add a Reference to the System.ComponentModel.Composition.dll assembly found in the bin directory of the MEF zip. 

Add

    1: using System.ComponentModel.Composition;

Now in the Program class, we need to import a value for Message -- that is, we want to specify that someone outside of this program needs to supply a message.  Then we need to remove our tight coupling.    Note in line 4-5 and we saying we want to import the value for Message.  Here I am showing doing it by type (string).. because basic types such as strings might be pretty common, consider using a named import such as [Import("Message")]

    1: class Program
    2: {
    3:  
    4:     [Import]
    5:     public string Message { get; set; }
    6:  
    7:     public void Run()
    8:     {
    9:        // SimpleHello hello = new SimpleHello();
   10:         //Message = hello.Message;
   11:  
   12:         Console.WriteLine(Message);
   13:         Console.ReadKey();
   14:     }

Now in the SimpleHello class we need to export the Message property.  This tells the system it can be used to satisfy requirements.    Notice line 3 and 4 and am marking it with an Export attribute..  Again, this exports it by type (string in this case).  As with above, you might want to do it with an explicit name for a more real world example [Export("Message")]

    1: public class SimpleHello 
    2: {
    3:     [Export]
    4:     public string Message
    5:     {
    6:         get
    7:         {
    8:             return "hello world!!";
    9:         }
   10:     }
   11: }

Now we need to tell MEF to wire these up for us.

    1: public void Run()
    2: {
    3:     //SimpleHello hello = new SimpleHello();
    4:     //Message = hello.Message;
    5:     var catalog = new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly());
    6:     var container = new CompositionContainer(catalog.CreateResolver());
    7:     container.AddPart(this);
    8:     container.Compose();
    9:  
   10:  
   11:     Console.WriteLine(Message);
   12:     Console.ReadKey();
   13: }

In line 5, we create a catalog -- that tells MEF where to look for imports and exports.  In this case, we are saying the currently running assembly.  There are tons of different parts catalogs, we will look at some later and you can of course build your own.

in line 6, we create a Composition container-- this is effectively the soup that all the different parts will be wired up together.

In line 7, we add this instance of Program to the container so that its dependencies get wired up.

In line 8, we do the compose magic.  this is where the Message property of Program gets set. 

Notice, in this case the wire up is done by matching types (String to String)... clearly that isn't always the right way, we will look at other ways to wire up later.

Run it and you get the expected output of "hello world!".

Now let's add another message, just to be a little more fun... 

    1: public class MoreMessages
    2: {
    3:     [Export]
    4:     public string FunMessage
    5:     {
    6:         get
    7:         {
    8:             return "This is getting fun!";
    9:         }
   10:     }
   11: }

Now run.. It blows up!  Why?  Well, let's look at the exception:

System.ComponentModel.Composition.CompositionException Error : Multiple exports were found that match the constraint '(composableItem.ContractName = \"System.String\")'. The import for this contract requires a single export only."

From the error it looks like we provided too many ways to satisfy the Import... MEF didn't know which one to pick.  Of course you can programmatically get in there and help, you can also just remove the export from one of the messages... but more fun, you can actually tell MEF you are able to deal with zero or more results.  Change the Message property of Program as follows:

    1: [Import]
    2: public IEnumerable<string> Messages { get; set; }

Notice we changed the return type to be a collection of strings rather than just one string.

Now change the usage code slightly and we get:

    1: class Program
    2: {
    3:  
    4:     [Import]
    5:     public IEnumerable<string> Messages { get; set; }
    6:  
    7:     public void Run()
    8:     {
    9:         //SimpleHello hello = new SimpleHello();
   10:         //Message = hello.Message;
   11:         var catalog = new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly());
   12:         var container = new CompositionContainer(catalog.CreateResolver());
   13:         container.AddPart(this);
   14:         container.Compose();
   15:  
   16:         foreach (var s in Messages)
   17:         {
   18:             Console.WriteLine(s);
   19:         }
   20:  
   21:     
   22:         Console.ReadKey();
   23:     }

image

Wow -- we get both messages!  Pretty cool...

More Value of MEF

Ok, I think we can all agree that we added a little complexity if all we were going to do is factor what was in the same assembly. MEF really shines when you have separate independent groups working on different components.  By definition these are often in different assemblies with cross no dependencies.  To show how MEF supports this, let's add a new Class Library project to our solution.    Call it ExternalMessages and add a reference to the System.ComponentModel.Composition.dll assembly.

Add the following class.

    1: using System;
    2: using System.ComponentModel.Composition;
    3:  
    4: public class Class1
    5: {
    6:     [Export]
    7:     public string Message
    8:     {
    9:         get
   10:         {
   11:             return "I am starting to get it...";
   12:         }
   13:     }
   14: }

Now we need to wire up this class into the catalog...    Notice in line 6, we change the catalog to look in a directory for the parts... 

    1: public void Run()
    2:  {
    3:      //SimpleHello hello = new SimpleHello();
    4:      //Message = hello.Message;
    5:      var catalog = new DirectoryPartCatalog(@"..\..\..\ExternalMessages\bin\Debug");
    6:          // new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly());
    7:      var container = new CompositionContainer(catalog.CreateResolver());
    8:      container.AddPart(this);
    9:      container.Compose();
   10:  
   11:      foreach (var s in Messages)
   12:      {
   13:          Console.WriteLine(s);
   14:      }
   15:  
   16:  
   17:      Console.ReadKey();
   18:  }

Note: DirectoryPartCatalog also supports relative paths that will look for a path under the current AppDomain.CurrentDomain.BaseDirectory.  For example:
new DirectoryPartCatalog(@”.\extensions\”);

Run it and we get our new message! 

Cool, but we lost our old messages, and I kind of liked them too...  Well, luckily, we have an aggregate part catalog that can take parts from several sources.

    1: public void Run()
    2:  {
    3:      //SimpleHello hello = new SimpleHello();
    4:      //Message = hello.Message;
    5:      var catalog = new AggregatingComposablePartCatalog();
    6:         catalog.Catalogs.Add (new DirectoryPartCatalog(@"..\..\..\ExternalMessages\bin\Debug"));
    7:         catalog.Catalogs.Add (new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly()));
    8:      var container = new CompositionContainer(catalog.CreateResolver());
    9:      container.AddPart(this);
   10:      container.Compose();

Pretty cool, we now get all the messages!

image

Finally, just to bring up the point here... I created a bunch of different assemblies that exported Messages... All I need to do is point the catalog at them and go. 

image

    1: public void Run()
    2: {
    3:     //SimpleHello hello = new SimpleHello();
    4:     //Message = hello.Message;
    5:     var catalog = new AggregatingComposablePartCatalog();
    6:        catalog.Catalogs.Add (new DirectoryPartCatalog(@"..\..\..\ExternalMessages\bin\Debug"));
    7:        catalog.Catalogs.Add(new DirectoryPartCatalog(@"..\..\..\ExtraMessages"));
    8:        catalog.Catalogs.Add (new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly()));
    9:     var container = new CompositionContainer(catalog.CreateResolver());
   10:     container.AddPart(this);
   11:     container.Compose();
   12:  

See where I added them in line 7..    Now just copy the assemblies into this directory and they become available for this program to use! Notice how I don't need to change any of the logic of the core program as I add more and more extensions. 

image

Taking MEF to the Next Level

Above I showed the simplest scenario... let's get a bit more powerful.  If you pick apart the main program looking for tight coupling, that Console.WriteLine() will really stand out.. What if you want to log to a file, call a web service or print to HTML or WPF?   Tightly coupling to the Console does not make that easy.  How can we use the seperation of concerns principle and MEF to get rid of this tight coupling? 

First, we need to define an interface that describes the contract for outputting strings.  To ensure correct dependency management go ahead and create a new Library project called SharedLibrary, add this interface and reference this project from each of the other team projects. 

    1: namespace SharedLibrary
    2: {
    3:     public interface IOutputString
    4:     {
    5:         void OutputStringToConsole(string value);
    6:     }
    7: }

Now, back in the main program we can factor out the Console.WriteLine ()...

    1: class Program
    2: {
    3:     [Import]
    4:     public IEnumerable<string> Messages { get; set; }
    5:  
    6:     [Import]
    7:     public IOutputString Out { get; set; }
    8:  
    9:     public void Run()
   10:     {
   11:         //SimpleHello hello = new SimpleHello();
   12:         //Message = hello.Message;
   13:         var catalog = new AggregatingComposablePartCatalog();
   14:            catalog.Catalogs.Add (new DirectoryPartCatalog(@"..\..\..\ExternalMessages\bin\Debug"));
   15:            catalog.Catalogs.Add(new DirectoryPartCatalog(@"..\..\..\ExtraMessages"));
   16:            catalog.Catalogs.Add (new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly()));
   17:         var container = new CompositionContainer(catalog.CreateResolver());
   18:         container.AddPart(this);
   19:         container.Compose();
   20:  
   21:         foreach (var s in Messages)
   22:         {
   23:             Out.OutputString(s);
   24:         }
   25:  
   26:     
   27:         Console.ReadKey();
   28:     }

In line 6-7 we define Out and in line 23 we change from Console.WriteLine() to Out.OutputString().

Now in External Messages project add the following class

    1: [Export(typeof(IOutputString))]
    2: public class Class1 : IOutputString
    3: {
    4:     public void OutputString(string value)
    5:     {
    6:         Console.WriteLine("Output=" + value);
    7:     }
    8:  
    9:     

Notice here we are explicitly saying the expert type to be that shared interface.  Now when we run it we get:

image

To make things more interesting, lets add another implementation of IOutputString that is a bit more creative.

    1: [Export(typeof(IOutputString))]
    2: public class ReverseOutputter : IOutputString
    3: {
    4:  
    5:     public void OutputString(string value)
    6:     {
    7:         foreach (var s in value.Split().Reverse())
    8:         {
    9:             Console.ForegroundColor = (ConsoleColor)(s.Length % 10);
   10:             Console.Write(s + " ");
   11:         }
   12:         Console.WriteLine();
   13:     }
   14: }

Just running this now would give us an error right, because we told MEF we wanted exactly one IOutputString... if we change our code to work with multiple we get more fun!  In line 7 we changed to request a set of IOutputStrings and in line 19 we changed to loop through all the output devices. 

    1: class Program
    2: {
    3:     [Import]
    4:     public IEnumerable<string> Messages { get; set; }
    5:  
    6:     [Import]
    7:     public IEnumerable<IOutputString> OutputSet { get; set; }
    8:  
    9:     public void Run()
   10:     {
   11:         var catalog = new AggregatingComposablePartCatalog();
   12:            catalog.Catalogs.Add (new DirectoryPartCatalog(@"..\..\..\ExternalMessages\bin\Debug"));
   13:            catalog.Catalogs.Add(new DirectoryPartCatalog(@"..\..\..\ExtraMessages"));
   14:            catalog.Catalogs.Add (new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly()));
   15:         var container = new CompositionContainer(catalog.CreateResolver());
   16:         container.AddPart(this);
   17:         container.Compose();
   18:  
   19:         foreach (var Out in OutputSet)
   20:         {
   21:             foreach (var s in Messages)
   22:             {
   23:                 Out.OutputString(s);
   24:             }
   25:         }
   26:  
   27:     
   28:         Console.ReadKey();
   29:     }

Now when we run it we get all our messages in all our outputs..

image

Now the only real logic in our main is the nested foreach loops.  We may very well like to change that in the future as well... so let's see if we can abstract that out using exactly the same techniques we have talked about already. 

    1: class Program
    2: {
    3:     [Import]
    4:     public IEnumerable<string> Messages { get; set; }
    5:  
    6:     [Import]
    7:     public IEnumerable<IOutputString> OutputSet { get; set; }
    8:  
    9:     [Import("OutputMessages")]
   10:     public Action<IEnumerable<IOutputString>, IEnumerable<string>> OutputMessages { get; set; }
   11:  
   12:     public void Run()
   13:     {
   14:         var catalog = new AggregatingComposablePartCatalog();
   15:            catalog.Catalogs.Add (new DirectoryPartCatalog(@"..\..\..\ExternalMessages\bin\Debug"));
   16:            catalog.Catalogs.Add(new DirectoryPartCatalog(@"..\..\..\ExtraMessages"));
   17:            catalog.Catalogs.Add (new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly()));
   18:         var container = new CompositionContainer(catalog.CreateResolver());
   19:         container.AddPart(this);
   20:         container.Compose();
   21:  
   22:  
   23:         OutputMessages(OutputSet, Messages); 
   24:         
   25:     }

First, in line 9-10 we define an Action that can do the output... and in line 23 we remove our nested foreach loops and replace it with a call to this method.  Now all we have in our Main is effectively wiring up components.  Very loosely coupled!

Now let's add an option for how the messages are outputted.   It would be very easy to define a config file option for selecting these and returning the correct one.  Notice that MEF knows that this method follows the contract of the Action delegate above, so it does all the wireup for us. 

    1: [Export("OutputMessages")]
    2: public void OutputByMessage(IEnumerable<IOutputString> outputSet, IEnumerable<string> messages)
    3: {
    4:     foreach (var s in messages)
    5:     {
    6:         foreach (var Out in outputSet)   
    7:         {
    8:             Out.OutputString(s);
    9:         }
   10:     }
   11:     Console.ReadKey();
   12: }

I would love to hear what you think?  Can you think of usage for MEF in your applications?    I'd also love for you to grab the final sample and see how many different ways you can extend it..  Here is the final working sample.  Enjoy!