Tuesday, April 6, 2010

Ten Reasons to use the Managed Extensibility Framework

Leer: Diez razones para usar MEF.

One question I'm commonly asked is, "Why do I need the Managed Extensibility Framework? How do I know when to use it?" In addition to that really being two questions, I'm not sure I can tell you the top ten reasons to use MEF or whether or not it is the right tool for you. Instead, as a hands-on consultant who has been using MEF in production applications since the earlier previews, I can share ten reasons why I've used it in my development projects and prefer it over over other frameworks.

If you can relate to these ten items and are interested in learning more, check out my video series, Fundamentals of the Managed Extensibility Framework. Click the link to preview the lessons and learn more!

Reason One: It's out of the box.

While much of my work with MEF has been using the previews in Silverlight 3 projects, MEF is not just another side project or add-on. MEF will be part of the .NET Framework 4.0 Common Language Runtime (CLR). Just as we've grown accustomed to using generic lists or sending mail with the SmtpClient class, the Managed Extensibility Framework will be there, in the framework, out of the box. Developers will be able to simply include the namespace and go to work. No additional installs or grief over whether or not to include the bits with the application and worry about versions. It's an intrinsic part of the .NET framework which I believe will lead to fast adoption for the myriad problems it solves. I always prefer to use what is already there when I can, rather than tacking on third-party solutions unless they are absolutely needed.

Reason Two: Dependency injection and inversion of control.

This is the part that seems to confuse people when they ask, "Do we need another Unity?" MEF is far more than a substitute for dependency injection or inversion of control. However, it does manage DI and IoC quite well. With Unity, for example, I had to wire in a bootstrapper and configure the container, then either grab objects from the container to take advantage of constructor injection or explicitly request objects.

In MEF, the parts model makes it far easier in my opinion. I simply make IFoo available:

...
[Export(typeof(IFoo))]
public class Foo {}
...

And then ask for IFoo, and I'm practically done!

...
public class Bar 
{
   [Import]
   public IFoo Foo { get; set; }
}

I find this far easier to set up and manage consistently across large applications than worrying about when/where/how the container has been configured, whether or not I'm using a nested container, and then how to grab the desired implementation.

It also makes testing a breeze. I don't believe in unit testing multiple layers at the same time - if I'm testing Bar, I'm not going to test Foo again. So in this case, I can just set IFoo to a mock object and focus on Bar.

Reason Three: Lifetime management.

By default, imports will provide me with the same (shared) copy of classes that are exported by MEF. I'm not locked into this, however. I can easily override the lifetime of a class using a PartCreationPolicy. This is very important to me because often classes become mangled in order to support patterns like the singleton pattern: you have to hack the constructor and make it private, then supply a public method, and make all of this work in concert with your interface.

With MEF, I simply build a class and don't build lifetime management into the behavior of the class. Should the class know or care if it will live as a single copy, or have multiple copies running around? In most cases, the answer is, "No."

Reason Four: Configuration.

A side effect of lifetime management is that MEF gives me a very easy way to manage configuration information. I've seen it done many ways. In Silverlight, for example, you might be tempted to write a static class or even add properties to the main Application class to hold application-wide configuration data. To me, mangling the application itself in order to support this is wrong.

With MEF, I can simply implement an IConfiguration interface and import it wherever I need it. If I'm writing WPF or ASP.NET code, it's a simple matter in the implementation to parse an app.config or web.config file to load up the values. The consumers of this information, however, don't have to know (and quite frankly, don't care) how it got there. I have a setting for number of threads? Great, I import my IConfiguration and read that property. Done deal.

This is also great for testing, because I can supply an alternate configuration for my test environment.

Reason Five: Factories with dependency resolution.

This ties back to the part creation policies. One powerful feature of MEF is the concept of an ExportFactory. Unlike a direct import, an import of ExportFactory<T> doesn't give me T, but instead gives me a way to create T. This is important if I have a class that might get created multiple times.

If the class has dependencies, such as the configuration information I gave in the previous point, we need MEF to import and satisfy those dependencies. With the export factory, I can do this:

...
[Import]
public ExportFactory<IContract> Factory { get; set; }

public IContract MakeANewContract()
{
   return Factory.CreateExport().Value; 
}
...

Each time I make a new contract, all of the dependencies are wired in for me, so I don't have to worry about loading up the constructor or walking the dependency tree. MEF does it for me. For an example of this, take a look at my blog post Sequential Asynchronous Workflows using Co-routines (the view factory to "make" squares follows this pattern, as it must import the random number service).

Reason Six: ImportMany.

A common pattern in Silverlight and WPF applications is to have a region for some functionality, and then page through multiple controls within that region. For example, a wizard might occupy the main region of the page but actually use four different controls for each of four different steps in the wizard. MEF makes it easy to handle this pattern using ImportMany. I can ask for an IWizard and then retrieve a list of controls that satisfy the interface.

Another example is chain of responsibility and pipeline. The "weak link" in the chain is often that first "bootstrapper" class that has to wire in all of the links in the pipeline or chain of responsibility. The common implementation of this pattern is to have each link process and then conditionally pass processing along to the next link.

With MEF, I can easily import many instances of ILink and then either chain them together or use an iterator or other means to walk through the processes. Adding a new link is as simply as building the implementation, then exporting the class.

These are just a few examples of how powerful the ability to have multiple imports is.

Reason Seven: Strongly typed Metadata.

Of course, you might want to call classes in a particular order. Perhaps you export user controls and use an enumeration to specify where on the screen it belongs. Whatever your needs are, MEF allows you to supply strongly-typed metadata for your exports.

For an example of this, take a look at MEF instead of Prism for Silverlight. There I use the metadata to specify the types of views and regions, then wire them together using the region management pattern introduced by Prism.

Metadata can provide initialization information, routing information, and even priority information. For the Vancouver Winter Olympics, I built a synchronization tool that would scan primary and failover servers for live streaming. The tool generated lists of mismatched and/or missing files so the team at Microsoft could quickly react to ensure the integrity of the failover cluster and guarantee uptime. The tool created an XML document with the changes, then passed the document through a pipeline of modules. One module persisted the file to disk and updated a manifest. Another module would generate a CSV file, and yet another could email the document if certain alert criteria were met. I used MEF to pull in the modules, and metadata to tag the priority (for example, I wanted to persist the file to disk before specifying it as an attachment in the email program, so the module that saved the file had a higher priority than the module that would email it). When I needed another module, such as in testing, it was as simple as building it and exporting it to inject it into the pipeline.

Reason Eight: Extensibility.

MEF is unique in that it is designed specifically for extensibility. Many Dependency Injection/Inversion of Control solutions are designed to help you wire up what you know - "I have this instance, map it to this interface." MEF is designed to help you wire up what you don't know. It is extensible by nature and contains features such as stable composition that ensure your application can safely extend in realtime.

As an example (and for a fast introduction to MEF if this is new to you) take a look at my video, Build a Plug-in Architecture in Under 10 Minutes. I demonstrate building a console application using MEF. After I have the application running, I build a plugin, drop it into the target directory, and show how the application is able to pull in the new plugin in realtime without restarting. Now that's powerful!

Reason Nine: Modularity.

Along with extensibility comes modularity. This is especially important for Silverlight applications. Because these run in the user's browser, developers must take care to minimize the size of the file download as well as the client footprint. You don't want an application that takes 15 minutes to download or maxes out your customer's memory and CPU.

MEF allows for modularity by allowing you to separate functionality into different modules and load them dynamically as needed. This is done via the DeploymentCatalog. Imagine a large application that has ten menu items, but users typically may only go into one. This was the case for our Winter Olympics application that helped the Microsoft team monitor their environment. At any given time they might be pushing a configuration file, monitoring the live streaming servers, or performing other functions within the tool that were not interdependent on each other.

Instead of loading everything and having a huge XAP file, MEF allows you to split your applications into modules that load on demand. Only when the user needs a piece of functionality will their browser download the extension and make it available. This allows them to get up and running faster and conserves resources by only supplying what is needed.

Take a look at MEF instead of Prism for Silverlight Part 1 for an example of dynamic module loading.

Reason Ten: Customization.

Finally, I prefer MEF because it allows for customization. All of the parts are available to extend or customize as needed. Perhaps you are a fan of convention-based configuration and would rather use a provider-based model to wire in dependencies. It can be done.

I develop in Silverlight and am a fan of creating reusable behaviors to avoid duplicating code-behind or overloading view models where it's not needed. With MEF, this is very easy and straightforward. Take a look at Custom Export Providers with Custom Metadata for Region Management as an example. Using metadata, I wanted to take regions and route views similar to how Prism does it. Because MEF is extensible and customizable, I was easily able to build a behavior that would allow me to tag regions and export them with strongly-typed meta data in the XAML.

Of course, the customization doesn't stop there. Take a look at the MEF Visualizer Tool that can be injected into your project and generate DGML to visualize the MEF parts. Now that's powerful!

Conclusion

I'll conclude by saying that I'm very biased toward MEF. For good reason: I've been using it in enterprise applications and it has enabled me to build more composite, extendable, testable, and scalable applications faster and, in my opinion, better. If you're sitting on the fence wondering how MEF compares to other frameworks, I hope this guide will help you decide just how powerful it can be and how it might better fit your needs.

Please share your comments and feedback below. I'd especially love to hear if you are currently working on a MEF project and would like to share how it has helped (or perhaps hindered) you in the process. Thanks!

Jeremy Likness