Dependency Injection in the Managed Extensibility Framework – Optional Injection

This is part two 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

Last time I showed you how the CompositionContainer can be used to compose or bind components together to fulfill dependencies. This time, I’m going to show how you can specify optional dependences.

Now let’s take the same ConfigurationService component that we used last time, and make a small change:

public class ConfigurationService
{
    [Import]
    public IConfigurationStore Store
    {
        get;
        set;
    }

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

    public void Load()
    {
        if (Store == null)
            Store = new DefaultConfigurationStore();

        Settings = Store.LoadSettings();
    }    
}

The new changes are in bold italics in the Load method. Basically what we’re saying is, that if we never end up being set a configuration store, we’ll fall back to using the default one.

Now based on that, let’s try and remove the registration of the RegistryConfigurationStore so that we’re no longer have a configuration store in the container, and see what happens when we bind:

    CompositionContainer container = new CompositionContainer();

    // Register imports
    ConfigurationService configurationService = new ConfigurationService();
    container.AddComponent(configurationService);
    // Match exports to imports
    container.Bind();

Attempting to bind now, throws something similar to the following:

System.ComponentModel.Composition.CompositionException: There was at least one composition issue of severity level `error'. Review the Issues collection for detailed information.       

Looking at the CompositionException.Issues collection, we get the following errors:

Error : There were no exports found with the contract 'IConfigurationStore'.
Error : A failure occurred while trying to satisfy member 'Store' on type 'ConfigurationService' while trying to import value 'IConfigurationStore'. Please review previous issues for details about the failure.

Okay, so that makes sense. We’re no longer satisfying the import on ConfigurationService, so CompositionContainer.Bind fails to bind the added components (in this case, just the single one added; ConfigurationService).

Now, because ConfigurationService can actually function without being set a IConfigurationStore, we can use the named argument IsOptional on the Import attribute to mark it as being optional:

public class ConfigurationService
{
    [Import(IsOptional=true)]
    public IConfigurationStore Store
    {
        get;
        set;
    }

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

    public void Load()
    {
        if (Store == null)
            Store = new DefaultConfigurationStore();

        Settings = Store.LoadSettings();
    }    
}

Now let’s see what happens when we bind:

    CompositionContainer container = new CompositionContainer();

    // Register imports
    ConfigurationService configurationService = new ConfigurationService();
    container.AddComponent(configurationService);
    // Match exports to imports
    container.Bind();

And we see that the bind was successful.

The above has shown you how you can use ImportAttribute.IsOptional to indicate to the CompositionContainer that a particular import does not need to be satisfied by an export for a bind to be successful. Next time I’ll did in deeper into the [Import] and [Export] attributes, including importing collections and named imports.

Published Thursday, June 12, 2008 7:00 AM by David Kean
Filed under: