- The Problem: Decoupling
- The Solution for Decoupling: Dependency Injection
- Microsoft's Answer: The Managed Extensibility Framework
- More than Meets the Eye
- Wrapping Up
Microsoft's Answer: The Managed Extensibility Framework
Released in the .NET 4.0 Framework, and available under the Microsoft Public License (Ms-PL) for the .NET 3.5 Framework, the Managed Extensibility Framework (MEF) is another way to solve the problem of dependency resolution. MEF is designed to make composite, modular, and extensible applications easy to build.
Conceptually, inversion of control in MEF works through constructs known as parts. A part has three distinct pieces:
- The contract
- A set of imports (components that use the contract)
- A set of exports (components that satisfy the contract)
In the travel agency example, the contract for booking a flight can be considered a part. The class that uses the contract will provide the import, and the components for booking over phone and on the Internet provide the exports (see Figure 2).
Figure 2 The travel agency part for MEF.
You can think of parts in terms of supply and demand. Our component has the demand, so it must import product to satisfy the demand. To import a contract, we simply expose a property for the contract and decorate it with the Import attribute. To better illustrate this principle, let's create a "Hello, MEF" application.
Start by opening Visual Studio 2010 and creating a new console project. To use the Managed Extensibility Framework, you must add a reference using the .NET tab to the namespace System.ComponentModel.Composition. Now we're ready to get started.
The goal of our program is to display the text "Hello, World." However, the program doesn't know how this will be done. It has a single responsibility, and that is to provide the text to show. The implementation of how the text will be shown is handled elsewhere.
Our contract for displaying text is simple:
public interface IDisplayText { void ShowText(string text); }
The main program uses this contract to send out the "Hello, World" message. It demands the implementation by importing it:
class Program { [Import] public IDisplayText DisplayService { get; set; } public void HelloWorld() { DisplayService.ShowText("Hello, World."); } static void Main(string[] args) { var p = new Program(); p.HelloWorld(); } }
Next, we'll create the class that honors the contract. This is the supplier, and we use the export attribute to indicate that we're satisfying the contract.
[Export(typeof(IDisplayText))] public class ConsoleDisplay : IDisplayText { public void ShowText(string text) { Console.WriteLine(text); } }
We've just created a part! Remember, a part includes a contract, imports, and exports. Our part includes the following contract, import, and export:
- Contract: IDisplayText
- Import: DisplayService property of the Program class
- Export: ConsoleDisplay class
The final step is to perform the dependency injection. MEF does this through the use of catalogs. If you think about parts as supply and demand, think of catalogs as shoppers and grocery stores. Catalogs help MEF to decide where to look for imports (the shoppers who demand a product) and exports (the suppliers who manufacture the product).
We'll create a simple catalog that scans an assembly for parts, and then we'll pass it the assembly we've created. Once MEF knows the catalog, we can ask it to "satisfy" the parts by matching exports with imports.
Here's the revised code to perform the step of injecting the dependencies (which MEF calls composition):
static void Main(string[] args) { var p = new Program(); var catalog = new AssemblyCatalog(typeof (Program).Assembly); var container = new CompositionContainer(catalog); container.ComposeParts(p); p.HelloWorld(); }
Executing this code will provide the desired result: The text Hello, World is sent to the console because the contract was satisfied by the console component.
This may seem like several steps to match an implementation to an interface, but remember that MEF satisfies imports hierarchically. When components require multiple contracts and implementations require even more contracts, MEF walks through each component recursively and satisfies all requirements. This is incredibly powerful, enabling the flexibility we need when building decoupled systems.