Replacing components with MEF

What I’m trying to do

Not many businesses in the world operate in the very same way. When you’re selling a line of business application, more often than not, the standard implementation does not exactly match what the customer wants. Providing the application with a wealth of configuration options helps, but you can only offer as an option what you can foresee; and sometimes people have surprising ideas. Yes, some of these ideas are bad and it’s best to talk the customer into an area of sanity again, but in an equal number of cases, you just didn’t think of it.

So to make sure that your application is going to cope with unforeseen change, it’s good practice to make parts interchangeable, and Dependency Injection is the agreed tool for accomplishing this. The metaphor of an interchangeable part is simple enough: you’ve got an interface, and two implementations for it. The implementation that ships with the standard product can be exchanged with a custom one.

Interesting enough, a lot of Dependency Injection Frameworks (“DIFs”) do not really shine when you want to accomplish this. The problem is that a lot of DIFs use the interface of the component as the key in their catalog, and do not allow two identical key, with the result that you cannot have two implementations of the same interface. MEF has a slightly different reason, but puts up some resistance nonetheless. What I’m trying to build here is a way to provide frictionless replaceable components with MEF.

Define a priority for exports

Here we’re got an interface for an add-in component, and a class that uses it:

  public interface IMyAddin
  {
  }

 public class Consumer
 {
    [Import]
    public IMyAddin MyAddin { get; set; }
  }

In a lot of DI frameworks, having multiple implementations for a single interface is not possible: the DI container simply would not know which one to choose. MEF however does not have the slightest problem with multiple exports for the same contract. In fact, having multiple parts with the same export definition is how you can use MEF to provide different components via a plug-in interface.

In order to let an import receive multiple components, an [ImportMany] attribute is applied to a property that is set up as an enumeration. The class that declares the ImportMany import can then decide which of the implementations to use or instantiate. This is a very powerful concept, and I like it quite a lot that there explicit control – MEF provides the infrastructure, but does not force business logic into the framework.

  [ImportMany]
  public IEnumerable<IMyAddin> AllMyAddins  {  get; set; }

In the scenario of this article however, each and every dependency setter property would need to be declared as a collection, marked with [ImportMany], and there would need to be imperative logic that choses the right implementation for the collaborator. This is obviously a lot of overhead, and worse: forces infrastructural responsibilities into business logic. Nope, not a good idea. The import properties should not need to change.

If the imports cannot change, we’ve got the exports. In our scenario, the implementation of an interface either belongs to a standard product or a customization. The exported component knows what it is: it can declare itself to be either a low-priority standard implementation, or a high-priority customization.

Now MEF supports Metadata that describes additional, user-defined properties for exports. The metadata consists of simple key-value pairs, and is defined as an additional attribute. So we can mark exports with a priority by providing metadata:

  [Export( typeof( IMyAddin ) )]
  [ExportMetadata( "Priority", 0 )]
  public class LowPriorityAddin : IMyAddin
  {
  }

  [Export( typeof( IMyAddin ) )]
  [ExportMetadata( "Priority", 5 )]
  public class HighPriorityAddin : IMyAddin
  {
  }

Choosing the export with the highest priority

So what we’ve got is the ability to declare the priority of an export, and an import that the part with the highest priority is assigned to. Someone needs to decide which is the that part.
Between the export and the import, there is the catalog, and the container. The catalog has an extensibility point that is very well suited. All catalogs derive from the abstract base class ComposablePartCatalog, which provides a virtual method GetExports: it returns the parts that match an import definition. By overriding this method, we can manipulate the assignment to an import so that when multiple exports are defined, only the one with the highest priority is returned. So:

Let’s build a catalog.

This implementation of a MEF catalog provides some logic to look up an export’s priority, and if there is more than one of the same contract, only returns the one with the highest priority. If no priority is defined on an export, it counts as a zero.

using System;
using System.Linq;
using System.ComponentModel.Composition.Primitives;
using System.Collections.Generic;

namespace GreenIcicle.ReplacementExport
{
  /// <summary>
  /// An implementation of a MEF part catalog that lets parts of the same 
  /// contract be replaced with other implementations based on a Priority
  /// of the export.
  ///</summary>;
  /// <remarks>;
  /// This is a decorator around an existing catalog.
  /// </remarks>
  public class ReplaceablePartCatalog : ComposablePartCatalog
  {
    private ComposablePartCatalog m_Catalog;

    /// <summary>
    /// Creates a new ReplaceablePartCatalog around an existing catalog.
    /// The catalog that this class decorates is provides in the constructor
    /// paramater "catalog".
    /// <summary>
    public ReplaceablePartCatalog( ComposablePartCatalog catalog )
    {
      m_Catalog = catalog;
    }
	
    /// <summary>
    /// Returns the exports in the catalog that match a given definition of an import. 
    /// This method is called every time MEF tries to satisfy an import.
    ///</summary>
    public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports( ImportDefinition importDef )
    {
      // We only need to care about replacements when the import 
      // requires just one export to be satisfied - in the case that an
      // ImportMany is defined, use the standard bahavior and return 
      // all matching exports.
      if( importDef.Cardinality != ImportCardinality.ExactlyOne )
      {
        return base.GetExports( importDef );
      }
      
      // We know we need to return exactly one import, now find the matching 
      // tuple of a part and its export definition
      Tuple<ComposablePartDefinition, ExportDefinition> matchingExport = null;
      int matchPriority = -1;

      // Walk through all parts in that catalog...
      foreach( ComposablePartDefinition partDef in Parts )
      {
        // ... and for each part, examine if any export definition matches the 
        // requested import definition.
        foreach( ExportDefinition exportDef in partDef.ExportDefinitions )
        {
          if( importDef.IsConstraintSatisfiedBy( exportDef ) )
          {
            // See if there is a Priority defined in the export's metadata.
            // If this is the case, replace the current matching export
            // if it has a lower priority. If there 
            // is no priortity defined, assume a value of 0.
            object priorityMetadata;
            exportDef.Metadata.TryGetValue( "Priority", out priorityMetadata );
            int priority = ToInt( priorityMetadata );

            // If the priority of the current export is equal to the 
            // previously found export,  remove it: in this case 
            // there are two exports with the same priority, and 
            // it's not possible to decide which one to use.
            if( priority == matchPriority )
            {
              matchingExport = null;
            }

            // If the priority of the current export is better than 
            // the previously found export, replace it. 
            if( priority > matchPriority )
            {
              matchingExport =  new Tuple<ComposablePartDefinition, ExportDefinition>( partDef, exportDef );
              matchPriority = priority;
            }
          }
        }
      }      

      // The return value of this method is an enumeration with one element, 
      // or when no matching export could be found, zero elements.
      IList<Tuple<ComposablePartDefinition, ExportDefinition>> result 
        = new List<Tuple<ComposablePartDefinition, ExportDefinition>>();
      if( matchingExport != null )
      {
        result.Add( matchingExport );
      }
      return result;
    }

    /// <summary>
    /// Returns the parts available in the catalog.
    /// </summary>
    public override IQueryable<ComposablePartDefinition> Parts
    {
      get
      {
        return m_Catalog.Parts;
      }
    }

    /// <summary>
    /// Converts an untyped value into a int. If the object is null
    /// or cannot be converted to an int, returns 0 (zero).
    /// </summary>
    protected static int ToInt( object value )
    {
      if( value == null )
      {
        return 0;
      }

      int result = 0;
      Int32.TryParse( value.ToString(), out result );
      return result;
    }
  }
}

The lame part about this catalog is that it handles multiple exports with the same priority as if there if there was not a single one, obfuscating the real cause of the problem. If you feel inclined to use this in some production code, you should change this; if you don’t, you’ve hereby be warned.

An interesting detail is that if the changed implementation of GetExports checks the ImportCardinality, and falls back to the base class implementation if not exactly one part is required to satisfy the import. This is done so that [ImportMany] imports still get all suitable parts, and not just the one with the highest priority.

Trying it out

Now we want to see the whole thing work. This test takes two classes that have the same export contract, and tries to satisfy a single property import; one of the exports has a higher priority ans wins.


  [Export( typeof( IMyAddin ) )]
  public class StandardAddin : IMyAddin
  {
  }

  [Export( typeof( IMyAddin ) )]
  [ExportMetadata( "Priority", 5 )]
  public class HighPrioAddin : IMyAddin
  {
  }

  public class Consumer
  {
     [Import]
     public IMyAddin MyAddin { get; set; }
  }

   [TestClass]
   public class ReplaceableContainerTest
   {
     [TestMethod]
      public void CreateContainer_ImportWithHighPriority_ReplacementWins()
      {
        // Arrange
        // -----
        TypeCatalog typeCat = new TypeCatalog( 
          typeof( StandardAddin ),
          typeof( HighPrioAddin ), 
          typeof( Consumer ) );

        ReplacablePartCatalog cat = new ReplacablePartCatalog( typeCat ); 
        CompositionContainer container = new CompositionContainer( cat );

        Consumer consumer = new Consumer();

        // Act
        // -----
        container.SatisfyImportsOnce( consumer );

        // Assert
        // -----
        Assert.IsNotNull( consumer.MyAddin );
        Assert.AreSame( typeof( HighPrioAddin ), consumer.MyAddin.GetType() );
      }
   }

woo-hoo! We can see that no CompositionException has been thrown (which is what happens when there is a problem with satisfying an import), and that a HighPrioAddin object has been created and assigned.

A better Export attribute

What I don’t like that far is that you have to define the Priotity metadata for every export, and that you have to type “Priority” right every time – an typo or otherwise invalid metadata declaration would not even lead to a runtime error, the priority would silently be set to zero.

Luckily, MEF supports custom export (and import) attributes. These are very easy to create: Derive from the base class of the attribute, and mark it with the [Metadata] attribute, and it’s possible to have a strongly-typed Priority value with a decent default value. When the attribute is used, MEF turns every public property of such a class into an entry in the export metadata dictionary. Please also note that the custom attribute is marked with “AllowMultiple = false”. The default is that the same attribute can appear multiple times – this would create multiple definitions of the priority, and hence, the metadata value in the dictionary would be an array of integer.

using System;
using System.ComponentModel;
using System.ComponentModel.Composition;

namespace GreenIcicle.ReplacementExport
{
  /// <summary>;
  /// Marks an export as a replacement for another export with the same contract.
  /// </summary>;
  [MetadataAttribute]
  [AttributeUsage(
   AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method,
   AllowMultiple = false )]
  public class ReplacementExportAttribute : ExportAttribute
  {
    #region Constructors
    public ReplacementExportAttribute()
    {
      Priority = 5;
    }

    public ReplacementExportAttribute( Type contractType )
      : base( contractType )
    {
      Priority = 5;
    }

    public ReplacementExportAttribute( string contractName )
      : base( contractName )
    {
      Priority = 5;
    }

    public ReplacementExportAttribute( string contractName, Type contractType )
      : base( contractName, contractType )
    {
      Priority = 5;
    }
    #endregion

    [DefaultValue( 5 )]
    public int Priority
    {
      get;
      set;
    }
  }
}

Using this custom attribute, these two export definitions become equivalent:

  [Export( typeof( IMyAddin ) )]
  [ExportMetadata( "Priority", 5 )]
  public class LowPriorityAddin1 : IMyAddin
  {
  }

  [ReplacementExport( typeof( IMyAddin ) )]
  public class HighPriorityAddin2 : IMyAddin
  {
  }

Rounding it up

So with quite little effort, we can customize MEF to use either one or another implementation of the same component, without changing any business logic code, and have just a nice attribute to differntiate between low-priority and high-priority exports.

Advertisements
About

Christian is a software architect/developer. He lives in Germany, reads a lot, and likes cycling.

Tagged with: , ,
Posted in Coding
One comment on “Replacing components with MEF
  1. […] which filters and prioritize design-time exports (the idea and part of the code comes from here). The overall goal of the strategy is to to replace a ServiceProvider implementation with a dummy […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: