How do I expose configuration information through MEF?

<Goal>Post in 15 mins or less :-)</Goal>

This question which recently popped on our forums is one of the common questions we hear from customers. In this particular case, wcoenen (the person in the forums) had recently used information that he wanted to pull from configuration and pipe to a part. He solved the problem by creating a special configuration provider service which he injected into the part to provide the config. The code for the consumer looked like this:

 [ImportingConstructor]
public RecentlyUsedTracker(IConfigurationProvider configurationProvider)
{   
  this.recentlyUsedFile = configurationFile.GetValue<string>("recentlyUsedTracker.File");   
  this.maxItems = configurationProvider.GetValue<int>("recentlyUsedTracker.maxItems");
}

As you can see above RecentlyUsedTracker takes an import of configuration provider. The solution above does work. The biggest issue wcoenen raised is that coupling of the part to the config provider makes testing more difficult. It requires a mock service to be passed in to every test. Another subtlety is that you won’t know if the data is actually present until the code executes.

An alternative approach, use property exports.

A different way to approach this problem is to actually export the individual configuration values. We can still use the generic configuration provider, but we can keep a nice separation of concerns.  The part can then import these values like any other import. MEF provides a very powerful facility for doing this called property exports. A property export is a property which is decorated with an export attribute. It is special because it provides a place for you to execute custom logic for retrieving a value and returning it to MEF, i.e. a custom factory. This is a different model than the traditional class-level exports which MEF is responsible for the creation . So using a property export we can easily surface configuration information and we can use any method we choose to get the value including the provider. For example, take a look at the code below.

 

 public class RecentlyUsedTrackerConfiguration
{
  private IConfigurationProvider _provider;
  [ImportingConstructor]
  public RecentlyUsedTrackerConfiguration(IConfigurationProvider provider)
  {
     _provider = provider;
     File = _provider.GetValue<string>("recentlyUsedTracker.file");
     MaxItems = provider.GetValue<string>("recentlyUsedTracker.maxItems");
  }
  [Export("RecentlyUsedTracker.File")]
  public string File {get;private set;}
  [Export("RecentlyUsedTracker.MaxItems")]
  public int MaxItems {get;private set;}
}

RecentlyUsedTrackerConfiguration IS a part as you can see it has an importing constructor which takes a provider. But instead of exporting itself, it exports it’s properties which use the provider to get the values. It then assigns a string contract to each export which correlates to the configuration item it is exporting.

Note: You can make this more compile safe, by using constants or creating custom export attributes

Now let’s take a look at our consumer.

 [Export]
public class RecentlyUsedTracker {
  [Import("RecentlyUsedTracker.File")]
  public string File {get;set;}
 
  [Import("RecnetlyUsedTracker.MaxItems")]
  public int MaxItems {get;set;}
}

RecentlyUsedTracker imports File and MaxItems. It’s nice and clean (aside from the attributes :-) ). We can now easily test RecentlyUsedTracker, as he has no dependency on a provider. All he cares about is that the exports he needs are in the container.

 

An additional benefit the above approach introduces.

If you read my my or Nick’s blog’s, then you’ve heard us talk about Stable Composition and Rejection. Parts that don’t have there required dependencies are rejected. That means that RecentlyUsedTracker will be rejected unless the config items he needs are present. It’s consistent with the way the rest of MEF works it ensures consistency of the system. Of course if you want to allow the part to be created even though the data is not present, you can always set the AllowDefault property on Import = true.

 

What else can use this approach for?

Using property exports has a million and one uses. Any time you need to integrate legacy or non-MEF components to MEF, property exports are usually a sure win! For example, let’s say you have an existing third-party class which you use in your system which is sealed. You can create that class in a property export and surface it to MEF. It’s also a great way to surface singletons that you don’t want parts to depend on. What do I mean? Check the code below as an example.

 public class ApplicationPart
{
  [Export]
  public Application App {
    get{ return Application.Current;
  }
}

Any questions?