Oh man, oh man. The .Net team has outdone themselves this time. Not only have they given us the sugar of null coalescing operators, covariance/contravariance, and optional parameters, they have handed us an outstanding framework we can use to decouple large systems.
Microsoft had something good going with late binding but the implementation of COM/DCOM was flawed. A global registry which allowed only one object of a type to be registered at a time was not very flexible. They finally got it right with MEF. You can now create flexible, maintainable, testable, and extensible components for your systems.
I'm going to show how you can do that using MEF and a few lines of code. First and foremost, MEF, in it's simplest form, is Dependency Injection (or DI). It's important to understand the concept of Inversion of Control (IOC) and DI before you can understand the benefits of MEF. You can probably still use it, but it's full power will not be realized until those key concepts are understood.
With that in mind I'm going to assume the reader knows something about those concepts and will be using terms without explaining them. To understand the mechanics of MEF is easy. It centers around three key concepts. There are exporters, importers, and containers. The exporters will export a type, usually in some executable form. The importers will import certain types. The container allows composition of exporters to importers. The container is the "glue" that brings the importers and exporters together.
Let's look at some sample code that imports a certain type:
[Import] private SomeType _objectToImport;
This example illustrates field injection. The Import attribute tells the container that this class is expecting a certain type. If the type is not specified in the attribute then it's assumed to be the type of the field that it decorates.
Here is another example that shows how to import many of a particular type:
[ImportMany] private IEnumerable _objectToImportList;
Again, this illustrates field injection but the difference is instead of expecting a single object I will be expecting a list of objects (0 or more) which is specified by the ImportMany attribute.
Now let's look at some code that exports an object:
// you can either specify a type, or allow MEF to reflect on the type [Export(typeof(SomeType))] public class SomeType { public string Name { get; set; } }Now, let's look at the glue code:
using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting;
...
[ImportMany] private IEnumerable _objectToImportList; // a catalog to aggregate other catalogs var aggregateCatalog = new AggregateCatalog(); // a directory catalog, to load parts from dlls in the Plugins folder var directoryCatalog = new DirectoryCatalog(pluginDirectory, "*.dll"); // an assembly catalog to load information about parts from this assembly var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); aggregateCatalog.Catalogs.Add(directoryCatalog); aggregateCatalog.Catalogs.Add(assemblyCatalog); // create a container for our catalogs var container = new CompositionContainer(aggregateCatalog); // finally, compose the parts container.ComposeParts(this); // at this point this._objectToImportList should be filled with SomeType objects // which are loaded from the various catalogs
And there you have it. Simple and sweet.
In my next installment I'll show how to use an interface in a common library to provide ultimate decoupling.
No comments:
Post a Comment