Introduction
What is your favorite program? Office, Visual Studio, Photoshop, World of Warcraft, Reflector?
What do they have in common? 99% of the most popular software available today is extendable in some way, shape or form. Surely you do not have to be extensible to be popular?
Is the software that you are currently writing extensible? If not, why not?
Extensibility in the past has been extremely hard, you had to do all the work yourself using reflection! Not impossible but in time you will learn that security, isolation, crossing application boundaries & lifetime management is extremely difficult to do yourself! Microsoft also released System.AddIn (Managed AddIn Framework). MAF did make it a little easier once you got your head round the pipeline… but still too difficult to easily add to your application…
The Managed Extensibility Framework (MEF) is a new library in .NET that enables greater reuse of applications and components. Using MEF, .NET applications can make the shift from being statically compiled to dynamically composed. If you are building extensible applications, extensible frameworks and application extensions, then MEF is for you.
Let's Get Started
I created a pretty simple clone of Window Live Writer. Live Writer has LOADS of plugins available. Each plugin should be capable of adding content to the document.
Contract
Composable Parts do not directly depend on one another, instead they depend on a contract, which is a string
identifier. Every export has a contract, and every import declares the contract it needs. The container uses the contract information to match up imports to exports. If no contract is specified, MEF will implicitly use the fully qualified name of the type as the contract. If a type is passed, it will also use the fully qualified name.
public interface IContentSource
{
string FriendlyName { get; set; }
Nullable<bool> CreateContent(System.Windows.Window owner, FlowDocument document);
}
Each plugin (Composable part) should have a FriendlyName
and add their content into the FlowDocument
(by calling CreateContent
).
Composable Part
A Composable Part is a composable unit within MEF. Composable Parts export services that other Composable Parts need, and import services from other Composable Parts. In the MEF programming model, Composable Parts are attributed with the System.ComponentModel.Composition.Import
and [System.ComponentModel.Composition.Export]
attribute in order to declare their exports and imports. A Composable Part should contain at least one export. Composable Parts are either added to the container explicitly or created through the use of catalogs. The default catalogs that MEF ship with identify Composable Parts through the presence of an export attribute.
public class InsertHelloWorld : RudiGrobler.Writter.Common.IContentSource
{
public string FriendlyName { get; set; }
public InsertHelloWorld()
{
FriendlyName = "Insert Hello World...";
}
public bool? CreateContent(System.Windows.Window owner, FlowDocument document)
{
document.Blocks.Add(new Paragraph(new Run("Hello World")));
return true;
}
}
This is a very basic plugin that only inserts “Hello World” into the FlowDocument
.
MEFing the Application
Currently our application is statically compiled. This works… but now I want to add plugins? Even worst… what if you want to add a plugin?
Only 3 changes are required to MEFify your plugin:
-
Before we can start using MEF, reference System.ComponentModel.Composition.dll (MEF Preview 3).
-
Decorate your plugin with an Export
attribute
[Export(typeof(RudiGrobler.Writter.Common.IContentSource))]
-
Use a common directory for the plugins (optional):
And that is it… pretty simple!
String vs Type?
In a typical reflection-based addin framework, the contracts
type is used to locate all the addins! This is fine if you have a static
language (like C#), but what if you want to develop your addin using IronPython? Or any other dynamic language? MEF tries to accommodate these languages by moving away from the traditional type based identification to a simple string
identifier!
Now we have some MEF-complaint plugins… how do we locate and use them?
Catalogs & Containers
A catalog is responsible for discovering extensions and the container coordinates creation and satisfies dependencies.
There are many ways of loading the composable parts… I will use the Import
attribute:
[Import]
public IEnumerable<rudigrobler.writter.common.icontentsource> Extensions { get; set; }
One of the value propositions of MEF's attributed programming model is the ability to dynamically discover exports via catalogs. Catalogs allow applications to easily consume exports that have self-registered themselves via Export
attributes. Below is a list the catalogs MEF provides by default:
ComposablePartCatalog catalog = new DirectoryPartCatalog("extensions", true);
MEF provides a few catalogs out of the box… We will be using the DirectoryPartCatalog
! The DirectoryPartCatalog
scans a specified directory (extension) for all the available parts.
CompositionContainer container = new CompositionContainer(catalog);
container.AddPart(this);
container.Compose();
The container is exactly what it sounds like! It's a container full of composable parts. The container is very similar to the IOC/DI containers like Unity. Calling Compose
on the container matches all the exported parts (have) with Imported parts (needs).
And that is all that is required to make your application extensible! Here is the list of found plugins:
MEF does an excellent job of making extensibility in software map to how we logically think about it: I have (export) you need (import).
Blogs