Dependency Injection in the Managed Extensibility Framework – Named Contracts

This is part three in a series of posts on dependency injection in the Managed Extensibility Framework (MEF). You can find other posts in the series here:

  1. Dependency Injection in the Managed Extensibility Framework – Simple Injection
  2. Dependency Injection in the Managed Extensibility Framework – Optional Injection
  3. Dependency Injection in the Managed Extensibility Framework – Named Contracts

In part one, I showed that you can specify a contract (the thing that binds the ‘import’ and ‘export’ together) by passing a type to the constructor of the [Import] and [Export] attributes. Types are not the only way contracts can be specified; they can also be expressed using strings. For example, taking our types from the previous post, we can replace the typeof(IConfigurationStore) contract, with the literal ‘ConfigurationStore’:

public class ConfigurationService
{
    [Import("ConfigurationStore")]
    public IConfigurationStore Store
    {
        get;
        set;
    }

    public IDictionary<string, object> Settings
    {
        get;
        private set;
    }

    public void Load()
    {
        Settings = Store.LoadSettings();
    }    
}
[Export("ConfigurationStore")]
public class RegistryConfigurationStore : IConfigurationStore
{
    public IDictionary<string, object> LoadSettings()
    {
        var dictionary = new Dictionary<string, object>();

        // Load from the registry
        [...]

        return dictionary;
    }
}

String contracts come in handy when you want to export a value whose type alone isn’t enough to uniquely describe or identify it. Primitives, such as int and string, typically fall under this bucket, however, string contracts can also be used to export any type.

As an example of exporting a string contract, the following shows a class that exports a database connection string under the contract ‘ConnectionString’:

public class ApplicationSettings
{
    [Export("ConnectionString")]
    public string ConnectionString
    {
        get { return "Data Source=.;Initial Catalog=Northwind;Integrated Security=True"; }
    }
}

One thing you may have noticed, is that the above type exports the value of a property, instead of an entire type as we’ve seen previously. This is perfectly legal. You can also export fields and methods, which I’ll dig into deeper in a future post.

There are also other ways that you can export values other than via the [Export] attribute. For example, the following shows the same connection string being exported by adding it directly to the container:

CompositionContainer container = new CompositionContainer();
// Export the connection string under contract 'ConnectionString'
container.AddValue("ConnectionString", "Data Source=.;Initial Catalog=Northwind;Integrated Security=True");

This is equivalent to the following example which adds the ApplicationSettings class, defined above, as a component:

CompositionContainer container = new CompositionContainer();
container.AddComponent(new ApplicationSettings()); 

Above, I’ve shown you how to specify contracts using strings, as well as exporting values by adding them to directly to the CompositionContainer. Next time I’ll talk about exporting and importing collections.

Published Friday, June 20, 2008 7:00 AM by David Kean
Filed under:

Comments

Friday, June 20, 2008 1:12 PM by Steve Degosserie

# re: Dependency Injection in the Managed Extensibility Framework – Named Contracts

Hi Dave,

What happens if several components export the same named contract, and one component imports this named contract (as a simple type, not a collection) ?

Our current behavior treats this as an error when you attempt to call CompositionContainer.Bind(). We're talking about this at the moment, and are thinking about providing a way on the Import to indicate whether or not we should just pick an export for you (ie with this option on, it wouldn't be an error, with it off - it would be). What do you think?

Wednesday, June 25, 2008 2:49 AM by Steve Degosserie

# re: Dependency Injection in the Managed Extensibility Framework – Named Contracts

I'm thinking that it's better to keep that case as an error case ... if no additional information is provided to the CompositionContainer. I think the Composition logic must stay predictable.

If you give a less predictable logic, there will always be a day where the container will pick up the wrong choice, which can lead to bugs difficult to find, and the developer not being confident in the container. ;o) lol

Additionally, you can provide a more deterministic way (override default logic) to tell the container which one to pick up, in an external configuration ... but certainly not hard-coded in an attribute or sthg similar.

Thanks your feedback Steve. It's unlikely that we will provide the option on the container itself - we want components to be able to freely move between containers without breaking. Having container-specific options leads to components that are tied to a particular container.
However, we are thinking about having the [Import] itself tell the container whether it expects there to be multiple exports floating around. This is still up in the air - however, our next CTP will likely include something along these lines.