Microsoft’s solution for dependency injection is called Managed Extensibility Framework (MEF). Dennis presented about this on the Techdays 2015. Now it is time to look at this myself with a simple demo app.
Demo app
I’ve created a demo app to play with MEF. The class diagram is below. Program in MEFDemo will create a MachineChecker (that takes INetwork as parameter in the constructor) and call the Check method.
Next I will show the different classes. A complete working sample is for download at the end of this post.
Util
The Network class implements the INetwork interface. By putting Export on the type MEF can handle it.
[Export(typeof(INetwork))] public class Network : INetwork { // implementation }
The implementation uses System.Net.Dns, but it’s irrelevant for my demo.
Proxy
Like above the MachineChecker class needs the Export attribute. To instruct MEF how to construct the class I need to put the ImportingConstructor attribute on the constructor, since there is always a default constructor.
[Export] public class MachineChecker { [ImportingConstructor] public MachineChecker(INetwork network) { // implementation } // implementation }
An interface would make replacing the MachineChecker easier.
Program
Now in the Main I’ll create a container to wire up the classes. Then pass the container to the Run method.
public static void Main(string[] args) { var catalog = new AggregateCatalog(); var assemblyDirectory = "."; catalog.Catalogs.Add(new DirectoryCatalog(assemblyDirectory)); var container = new CompositionContainer(catalog); Run(container); Console.ReadLine(); } public static string Run(CompositionContainer container) { var machineChecker = container.GetExport<MachineChecker>(); var check = machineChecker.Value.Check("localhost"); return check; }
The container.GetExport returns a Lazy as in lazy loading. The value property contains the object MEF has constructed with INetwork as parameter.
Unit test
Isolating object for unit testing is easy when using dependency injection. The ComposeExportedValue extension is in the System.ComponentModel.Composition namespace.
using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; private CompositionContainer CreateIsolatedContainer() { var fakeNetwork = MockRepository.GenerateStub<INetwork>(); var catalog = new TypeCatalog(typeof(INetwork), typeof(MachineChecker)); var container = new CompositionContainer(catalog); // inject the stub here container.ComposeExportedValue<INetwork>(fakeNetwork); return container; }
In the code coverage you’ll see that Network is not used since it is replaced by the stub. Also the Main method is not called by the unit tests.
Conclusion
Dependency injection makes applications easier to unit test. Now it is part of the .NET framework (since v4.0), that’s one reason less not to use it.
Nice and clear example!