No, I am not becoming a druggie, but I am becoming more extensible. MEF stands for the Microsoft Extensibility Framework. It is a new Framework born out of the PRISIM project. The goal of this project is to create new patterns and practices to be used within the .NET Framework to create more modular and maintainable code.
MEF drives at the notion of “composing” applications, as opposed to building them. If you think of an application as logically divided amongst various modules this notion of composing the pieces together makes sense. Add on top of that the ability to allow for solid decoupling of the layers through a Dependency Injection centric model and you are able to A) create application that can change without changing (follows the “O” principle from SOLID) and B) enforces the ideals of the “design by contract” idealology.
This notion of “composition” is not new, in fact it’s a core concept in many modern paradigms focused on OO languages. MEF, however, takes it a bit further. Consider the example:
The application is divided into two projects. The Program class creates an instance of the Application class and calls Execute.
The Application class is more interesting and shows the first signs of MEF being used in our program:
1: public class Application
2: {
3: [ImportMany] private IEnumerable<Lazy> operations;
4: private CompositionContainer _container;
5:
6: public Application()
7: {
8: // create the catelog
9: var catelog = new AggregateCatalog();
10:
11: // add our universal directory to the cateloyt
12: catelog.Catalogs.Add(new DirectoryCatalog(@"C:\HelloAssemblies"));
13:
14: // create the container from the catelog listing
15: _container = new CompositionContainer(catelog);
16: _container.ComposeParts(this);
17: }
18:
19: public void Execute()
20: {
21: Console.WriteLine("Beginning Execution");
22: foreach (var op in operations)
23: {
24: Console.WriteLine("Executing");
25: Console.WriteLine("{0} - {1}", op.Value.SayHello(), op.Metadata.Language);
26: }
27: Console.WriteLine("Execution Complete");
28: }
29: }
If you are familiar with Dependency Injection (DI) then the concept of a “Container” is not foreign to you. Containers are useful in that they allow us to specify configuration information and based on that information that will A) resolve interfaces to concrete classes and B) allow for automated maintenance of an objects life. MEF operates in a very similar fashion.
Using MEF you can specify a catalog which houses the ways in which MEF will seek to fulfill composable parts. In my example, I am specifying a directory in which I can drop assemblies that will be automatically included as part of the application by MEF. Those DLLs are then searched for any classes which specify the ExportAttribute.
In the example, you notice I use the ImportsManyAttribute with an IEnumerable containing “lazy” types. Lazy is a special new type in .NET4 that denotes something which will be determined later. In this case, Lazy will load objects which implement the interface ISayHello and provide metadata via the interface ISayHelloData. Metadata is extremely useful in MEF and allows the specification of important pieces of data to the object. Consider the following implementation of ISayHello where the class will say “hello” in Japanese:
1: [Export(typeof(ISayHello))]
2: [ExportMetadata("Language", "Japanese")]
3: public class JapanSayHello : ISayHello
4: {
5: #region Implementation of ISayHello
6:
7: public string SayHello()
8: {
9: return "Domo";
10: }
11:
12: #endregion
13: }
Use the ExportAttribute to specify the composable type (interface) you want to associate the class with. In this case, since JapanSayHello implements ISayHello, we will export it as such. ExportMetadataAttribute specifies the bits of metadata desired for this class. In my case, I have a very simple metadata defintion:
1: public interface ISayHelloData
2: {
3: string Language { get; }
4: }
You can clearly see the correlation between the properties in this interface and the metadata as specified above.
In addition to JapanSayHello, I also created the classes GermanSayHello and AmericaSayHello. The idea was to reach a place where I could add/remove DLLs from a directory and thus change the way the application runs, without changing code. Here is how I did that:
Putting it all together
One of the things I am coming to find with MEF is, everything is about how you organize. A MEF based architecture isnt something you just throw together, nor is it something that you use for EVERYTHING in your application. It, like most tools/paradigms, has a very specific calling and can really help an application if applied right. Its very much like DI in that respect. For example:
On a project we are working on I was asked to create a Queue service to allow end users to move the process of a long running task off of their personal machines an onto a more stable application server environment. Due to time constraints I was forced to use a Factory pattern to perform command –> class resolution, however, I still feel that MEF would’ve been a great tool to use here. Here is why: as new Queue Commands are created, the Factory class must be updated to allow the Queue to know what a number representing.
As time went on, this became more cumbersome, partly because the number of commands needed ended up being higher then expected. I analyzed MEF as a way to simple drop new command DLLs into a directory and automatically pick them up so they could be run. Unfortunately, since the commands called into a shared architecture and the system wasn’t really organized to support this implementation the proposal was rejected due to time constraints. However, if I could do things over again, I would certainly consider MEF as a way to make adding functionality to a process without changing the central process.
The following video shows the effect of the code above:
(click to see video)
I really like the idea of composing applications. It just makes more sense then trying to view application, especially complex ones, as a single monolithic unit. By breaking the application into smaller units, testing becomes easier, maintenance becomes easier, and code becomes cleaner. In addition, you could play with the idea of not just using the same business logic in multiple applications but the same composable part.