Introduction
The Managed Extensibility Framework (MEF) enables developers to extend their application without any configuration. It has many features, one of which allows for developers to use it as a Dependency Injection framework.
If you want developers to be able to extend your application using only source code, then that means you would have to modify your application to call their source code, but this is not a desirable scenario because for one you do not want to continually have to modify your application to accept new components.
One potential solution would be for you to create an interface that other developers who create extensions can create. This results in lower coupling, but it would still require the user to inform your application of these extensions by updating a configuration file. This can thus result in maintenance difficulties.
This would also make it difficult for the creators of extensions to your application to use their extensions in other applications, since to use your application they have to implement your specific interface.
MEF provides solutions to these problems, where it will automatically load classes or even other assemblies without you as the developer having to provide any configuration information--just put these components in a particular directory and they will be read automatically!
My plan for this tip is to first demonstrate Dependency Injection without using MEF, then show you the same application using MEF to help you gain a better understanding of what MEF is doing and how it saves you from writing code.
Creating the Application Without MEF
Dependency Injection is a specific form of Inversion of Control (IoC). Dependency Injection is a software pattern where the control of a particular function is determined not by the algorithm that is inside the function, but instead by an object that is passed in. It is useful, for example, when you would want the software to behave in one way while in production mode and a different way while being tested.
Consider this simple application that performs an operation twice using a Singleton, which uses the Lazy class to defer the creation of a resource-intensive object until the last possible moment, thus saving resources if it turns out that the creation of that object did not actually end up being required:
using System;
namespace DependencyInjectionSample
{
class Program
{
static void Main(string[] args)
{
IDoubleOperation doubleOperation = DoubleOperation.Instance;
Console.WriteLine("Result of double operation = " +
doubleOperation.PerformDoubleOperation(3, 5));
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
}
public interface IOperation
{
int PerformOperation(int a, int b);
}
public interface IDoubleOperation
{
int PerformDoubleOperation(int a, int b);
}
public class Addition : IOperation
{
private static Lazy<Addition> instance =
new Lazy<Addition>(() => new Addition());
public static Addition Instance
{
get { return instance.Value; }
}
public int PerformOperation(int a, int b)
{
return a + b;
}
}
public class DoubleOperation : IDoubleOperation
{
private static Lazy<DoubleOperation> instance =
new Lazy<DoubleOperation>(() => new DoubleOperation(Addition.Instance));
public static DoubleOperation Instance
{
get { return instance.Value; }
}
private IOperation operation;
public DoubleOperation(IOperation operation)
{
this.operation = operation;
}
public int PerformDoubleOperation(int a, int b)
{
return operation.PerformOperation(
operation.PerformOperation(a, b),
operation.PerformOperation(a, b));
}
}
}
Creating the Application with MEF
The main components of MEF are a catalog and a composition container. The catalog helps you to discover extensions and the container is used to coordinate creation and satisfy dependencies.
With MEF, you can use either import or export attributes, where an import attribute refers to data that will be brought into the application, and an export attribute refers to data that is output from the application. Using attributes is a type of declarative code, which in some ways is better than traditional procedural code, because declarative code focuses more on what you want the code to do rather than how it is done.
If your application has an import attribute, then it must be matched with an export attribute. The attributes are filled by the composition engine.
Consider this version of PerformDoubleOperation
, which uses MEF:
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace DependencyInjectionMefSample
{
class Program
{
static void Main(string[] args)
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
var container = new CompositionContainer(catalog);
container.ComposeParts();
IDoubleOperation doubleOperation = container.GetExportedValue<IDoubleOperation>();
Console.WriteLine("Result of double operation = " +
doubleOperation.PerformDoubleOperation(3, 5));
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
}
public interface IOperation
{
int PerformOperation(int a, int b);
}
public interface IDoubleOperation
{
int PerformDoubleOperation(int a, int b);
}
[Export(typeof(IOperation))]
public class Addition : IOperation
{
public int PerformOperation(int a, int b)
{
return a + b;
}
}
[Export(typeof(IDoubleOperation))]
public class DoubleOperation : IDoubleOperation
{
private IOperation operation;
[ImportingConstructor]
public DoubleOperation(IOperation operation)
{
this.operation = operation;
}
public int PerformDoubleOperation(int a, int b)
{
return operation.PerformOperation(
operation.PerformOperation(a, b),
operation.PerformOperation(a, b));
}
}
}
Summary
This tip/trick is meant to be a simple demonstration of how an application can be built without MEF, and then a comparison of how the same is performed using MEF features. As I learn more about MEF, I intend on adding more details to this tip. If there is anything in particular about MEF that you are interested in, please let me know and I will try to add more details and example on that aspect.
References