Home » Software Dev

MEF: Making Extensibility Fun

30. July 2009 by Martin Rue 4 Comments

Extensibility is important. Face it, unless you are able to easily extend the functionality of your application, you are going to be missing out on a big competitive advantage. If your application is easily extensible, you will be able to build new features and respond to competition more quickly. For most people, the word extensibility probably makes them think plug-in. But extensibility doesn’t have to be a plug-in. Consider this:

  • Internal Extensibility allows you to easily add new components to your application internally. Think of this as an architecture that allows you to privately add functionality in a well defined way. I’ll give an example of this shortly.
  • External Extensibility is probably what you first thought I meant - the traditional plug-in model that makes applications like FireFox so useful and popular by allowing third parties to contribute.

If you’re building a new web application, extensibility (in the plug-in sense) is not something you are likely to consider. I don’t want those pesky 14 year olds uploading their new executable plug-in that starts opening my server’s CD-ROM drive and flipping the screen. But, in the context of a web application, that’s not what I mean by extensibility.

Granny What if, for example, you are Rufus of Big Heads Inc – the lead developer of Big Heads’ flagship product Head Effects. Head Effects allows your Grandma to upload pictures of her grandchildren and apply morbid effects to their heads. Grandma loves to make young Jimmy green and give him extra over-sized body parts. But after three short months of using Head Effects, Grandma is starting to realise that all her grandchildren look the same again – and she desperately needs more options in her mutations panel. Like many other Grandmas across the country, she calls customer support to express how dissatisfied she currently is: I can’t even make little Gertrude’s eyes blood-red or make Fatima’s brain leak through her nose. ‘Morbidify’ is offering me more effects for 20% less at just $129.99 per month, so I want to close my account.

Following lots of customer complaints and the two-mutations-per-week release frenzy of the main competition Morbidify, the CEO of Big Heads Inc personally tasks Rufus with the job of making it easier to develop new mutations for Head Effects.

Rufus knows the only way to achieve this is to design the system so the mutations are completely isolated and create infrastructure to handle the enumerating, loading and maintaining of each one. What Rufus needs is an extensibility solution that allows him (or any number of other developers) to design, test and deploy the mutations independently and have the system just know about them.

Let’s give Rufus some ideas. What if there was a directory called MutationsRepo that served as the mutations repository. In this directory there could be any number of assembly (.dll) files, each representing one distinct mutation that is available to users of Head Effects. The application would periodically enumerate the mutations repository and load the newly added mutations. The current list of available mutations in Grandma’s mutations panel would update the minute a new assembly was added or removed from the repository.

There are 101 different ways of building the new system, but after listening to our suggestion, Rufus much prefers this one – you’ll have to just trust me. Rufus is really happy with the new design but he’s not quite sure how he’d go about making it come to life. Think of all the disappointed Grannies across the country; I'm sure you simply can’t bare the thought – let’s help Rufus out here.

The Managed Extensibility Framework (MEF) is a new component of the .NET framework, and in a unplanned twist of coincidence, MEF really helps with Rufus’ career-changing problem. MEF is easy - a part can either be imported or exported. For example, a Library object may have a collection of Book objects which is marked as a MEF import. At runtime MEF will load any parts that are marked as an export and are of the correct type (Book) and populate the collection for us. The collection of objects you’re working with at runtime is dynamically supplied by MEF and you don’t have to do a thing. Well, maybe one teeny-weenie thing, but we’ll get to that shortly.

It’s worth noting that this is nothing new. In the good old days of Win32, we could enumerate the files from a directory and pick up just the .dll files. We could then attempt to LoadLibrary/GetProcAddress each one, in search of some standard function. What makes MEF different is that the whole process is almost completely abstracted away and allows you to focus on your application logic – not the plumbing needed to load externally located code.

Rufus is really excited, take it from me. To Rufus, MEF sounds like a friction-free way to keep all of Head Effects’ mutations isolated and allow them to be developed independently. The thought of creating a simple class library project and just dropping a .dll into the MutationsRepo makes Rufus childishly excited at the prospect of being able to afford his gym membership this month.

The first thing Rufus creates is the interface that’ll describe how Head Effects will talk to each of the mutations.

   1:  public interface IMutation
   2:  {
   3:      string Mutate(string input);
   4:  }

IMutation describes an object capable of taking a string, presumably mutating it in some way and returning the mutated string back to us. The input/output could be an Image of course, or anything else – a string is just the easiest way to demonstrate the concept.

Next, Rufus creates a class that holds a collection of all the available mutations (which will be populated by MEF) and allows him to run them all on some input data.

   1:  public class MutationEngine
   2:  {
   3:      [Import]
   4:      public IEnumerable<IMutation> Mutations
   5:      {
   6:          get; set;
   7:      }
   8:   
   9:      public MutationEngine()
  10:      {
  11:          var catalog = new DirectoryCatalog(@"C:\MutationsRepo");
  12:          var container = new CompositionContainer(catalog);
  13:          var batch = new CompositionBatch();
  14:          batch.AddPart(this);
  15:          container.Compose(batch);
  16:      }
  17:   
  18:      public string PerformAllMutations(string input)
  19:      {
  20:          string data = input;
  21:   
  22:          foreach (var mutation in Mutations)
  23:          {
  24:              data = mutation.Mutate(data);
  25:          }
  26:   
  27:          return data;
  28:      }
  29:  }

The first thing to note is the Import attribute placed on the Mutations collection (line 3). The Import attribute basically says “Dear MEF, this data should be supplied using any parts you manage to import”. All we need to know is that this collection will contain a number of IMutation objects at runtime, ready for Grandma to use.

The interesting part is the stuff going on in the constructor (lines 11 through 15). Long story short, this is how you get MEF to supply your imports with anything it manages to find in the catalog. To be more specific, we’re telling MEF that it should look for exported parts in a directory, which is specified as C:\MutationsRepo. MEF is going to look in C:\MutationsRepo for parts that match any of our imports – in this case, anything that’s an IMutation type. The next four lines cause MEF to do its magic and load anything it finds. Once the constructor has executed, it’s safe to assume any IMutation objects that were found in the MutationsRepo have been loaded by MEF and now exist in our Mutations collection (line 4). For effect, imagine a rabbit emerging from a hat.

Finally, the PerformAllMutations method does what it says on the tin. PerformAllMutations runs each mutation on some input data. Notice that nowhere do we add any items to the Mutations collection manually. MEF does all the work and we don’t even have to know about it. Today it may load two mutations and tomorrow it may load eight – who cares.

Now that Rufus has created the MutationEngine class, it’s not difficult to respond to Grandma’s furious clicking of the Run All Mutations button.

   1:  MutationEngine engine = new MutationEngine();
   2:  var output = engine.PerformAllMutations(grandmasInput);

At the moment, however, no mutations have been created. There’s just no way Grandma is going to return to Head Effects without some new and exciting mutations, so Rufus creates two new class library projects and adds them to his solution.

The First Mutation

   1:  [Export(typeof(IMutation))]
   2:  public class Mutation1 : IMutation
   3:  {
   4:      public string Mutate(string input)
   5:      {
   6:          return input + " " + "[Mutation1]";
   7:      }
   8:  }

The Second Mutation

   1:  [Export(typeof(IMutation))]
   2:  public class Mutation2 : IMutation
   3:  {
   4:      public string Mutate(string input)
   5:      {
   6:          return input + " " + "[Mutation2]";
   7:      }
   8:  }

Nothing too ground-breaking here. Notice the attribute that decorates both of the classes. In exactly the same way as the Import was specified earlier on the Mutations collection, an Export is being specified for the two parts that are going to export their functionality. MEF simply matches these two exports to the previous import and populates the Mutations collection with these two objects for us.

Rufus now takes the compiled output (.dll assembly) of both class library projects and drops them into the MutationsRepo directory. When PerformAllMutations is called again, the Mutations collection, in a magical yet emotional event, contains two IMutation objects.

   1:  MutationEngine engine = new MutationEngine();
   2:  var output = engine.PerformAllMutations("Hello MEF");

You shouldn’t have a hard time working out why output now holds the value “Hello MEF [Mutation1] [Mutation2]”.

Conclusion

MEF makes extensibility fun and easy. MEF is way more than what you’ve seen in this simple example, but I find the simple example compelling. I will certainly use MEF to make my life easier in situations where I have isolated functional units that need a quick and easy deployment solution. My own use of MEF will be in creating a service application for scheduled background tasks in my asp.net web applications. I’m sick of creating a new windows service for each one.

What About Rufus?

MEF this MEF that, who cares – what happened to Rufus? Rufus built on the advice given here and created quite possibly the most extensible cranial mutation system you’ve ever seen. Grandma returned to Head Effects, and due to her community popularity and word-of-mouth, Head Effects acquired more blood-thirsty Grandmas than you could possibly imagine. The CEO of Big Heads Inc was very happy with Rufus’ solution, and despite the grudge he held from Rufus breaking the couch at the Christmas party, he decided not to fire him. Rufus decided not to renew his gym membership in order to maintain the stereotype and he lives on to be the subject of another story.

Comments

pingback
blog.cwa.me.uk said:

Pingback from blog.cwa.me.uk

Reflective Perspective - Chris Alcock  » The Morning Brew #401

Ed McPadden
United States Ed McPadden said:

Hey Martin ... great article!  I have to say that I laughed out loud with the Grandma/Head Effects story.  I don't think I've ever been so entertained by such an informative article.  I just subscribed to your blog ... keep it coming!!!

Martin Rue
United Kingdom Martin Rue said:

Hey Ed, thanks for the positive feedback. I'm pleased you enjoyed the tone of the article and I'm sure you've not heard the last of Rufus. Having taken a look at your own blog, I'd like to return the gratitude - your MEF post is excellent.

pingback
216.an74.com said:

Pingback from 216.an74.com

2000 Ford E450 Sale, 2000 Ford E450 Super Duty Intake K&n Air Intakes

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading