Dependency Injection in the Managed Extensibility Framework – Simple Injection
This is part one in a series of posts on dependency injection in the Managed Extensibility Framework (MEF). You can find other posts in the series here:
- Dependency Injection in the Managed Extensibility Framework – Simple Injection
- Dependency Injection in the Managed Extensibility Framework – Optional Injection
- Dependency Injection in the Managed Extensibility Framework – Named Contracts
As its default out-the-of-box programming model, the Managed Extensibility Framework (MEF) allows users to apply attributes to types to indicate to MEF dependencies they require and the ‘values’ that they provide. By using the types in the System.ComponentModel.Composition namespace, these dependencies can be automatically determined and satisfied through the use of dependency injection (DI).
As an example, let’s start off with an interface that defines a contract for loading configuration settings:
public interface IConfigurationStore
{
IDictionary<string, object> LoadSettings();
}
The following type, ConfigurationService, uses the above interface to load a dictionary of configuration settings. its Store property is marked with the [Import] attribute indicating that it requires an instance of IConfigurationStore to work correctly.
public class ConfigurationService
{
[Import]
public IConfigurationStore Store
{
get;
set;
}
public IDictionary<string, object> Settings
{
get;
private set;
}
public void Load()
{
Settings = Store.LoadSettings();
}
}
Now let’s say we have another type, RegistryConfigurationStore, which represents a store that loads configuration settings from the registry. It itself is marked with the [Export] attribute, indicating that it provides an implementation of IConfigurationStore.
[Export(typeof(IConfigurationStore))]
public class RegistryConfigurationStore : IConfigurationStore
{
public IDictionary<string, object> LoadSettings()
{
var dictionary = new Dictionary<string, object>();
// Load from the registry
[...]
return dictionary;
}
}
Okay, so we have two components; one that requires (‘imports’) an IConfigurationStore and one that provides (‘exports’) an IConfigurationStore. The next question is, given that they don’t know about each other, how do we export the IConfigurationStore from RegistryConfigurationStore to satisfy the import on ConfigurationService?
This is where the CompositionContainer fits in. The CompositionContainer provides a way to mesh a set of components together to match the exports on one component to the imports on another.
We start off registering the components in the container:
CompositionContainer container = new CompositionContainer();
// Register imports
ConfigurationService configurationService = new ConfigurationService();
container.AddComponent(configurationService);
// Register exports
container.AddComponent(new RegistryConfigurationStore());
Next, we call CompositionContainer.Bind which, using Reflection, discovers the export on RegistryConfigurationStore, and matches to the import on ConfigurationService.
// Match exports to imports
container.Bind();
Once Bind is called, the ConfigurationService is now ready to be used:
// Load the configuration services
configurationService.Load();
Now, you might be thinking that the matching of the export from RegistryConfigurationStore to the import on ConfigurationService.Store could have been entirely manually without the CompositionContainer, and with a lot less code:
ConfigurationService configurationService = new ConfigurationService();
configurationService.Store = new RegistryConfigurationStore();
configurationService.Load();
Now, this is a fine solution for small non-extensible applications, however, this approach breaks down when building open and dynamic applications. MEF shines when components need to be loaded on demand or when they are not known statically at compile time. In future blog posts, I’ll talk about our MEF provides infrastructure to dynamically discover components and its approach to delaying the creation of a component until absolutely needed.