Using MEF for classes which take configuration values

Jan 27, 2010 at 1:15 AM
Edited Jan 27, 2010 at 1:16 AM

When converting some existing code to make use of MEF, I sometimes encounter classes which take configuration values in their constructor arguments. I've come up with the following way to MEF-ify such constructors; essentially I pass a configuration provider instead of passing the configuration values directly:

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

However, now my unit tests are more complex: I need to mock IConfigurationProvider. This is doable thanks to mocking frameworks, but still annoying. Is there a better way?

Jan 27, 2010 at 1:30 AM

My personal preference is to import configuration rather than using a provider as you have shown. There are several different appproaches to surfacing configuration information through MEF as exports.

The easiest approach is to use property exports to provide the information. (see below)

public class RecentlyUsedTrackerConfiguration
{
  public RecentlyUsedTrackerConfiguration()
  {
     //set values here
  }

  [Export("RecentlyUsedTracker.File")]
  public string File {get;set;}

  [Export("RecentlyUsedTracker.MaxItems")]
  public int MaxItems {get;set;}
}

For example, above RecentlyUsedTrackerConfiguration provides configuration information through property exports. MEF pulls on the property getters and exports that info. If the part needs to be data drvien, you can still access a service behind the scenes in the getter (such as your provider) to get the info. The consumer though is completely insulated and simply imports....such as below.

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

RecentlyUsedTracker imports the config info above. Now for testing you just pass in the values you need in the test.

If the # of config keys is pretty well defined at compile time, this works very nicely. Also I would not recommend using magic strings, you can use consts, or even create custom import attributes that are strongly typed and specify the string in the base ctor.

There are alternative more sophisticated approaches such as using a custom ExportProvider, but for many scenarios this works fine.

Thanks

Glenn

Jan 27, 2010 at 12:24 PM

Thanks Glenn! In my first attempt my constructor actually looked just like your suggestion. However, I was adding the configuration values explicitly to the container at start-up which got unwieldy. Simply exporting those values by putting Export attributes on class properties works much better, I'm not sure why I didn't see that before :-)

Jan 27, 2010 at 5:20 PM

Yes, that is another approach :-)

The advantage of this is a. It does not require the "configuration parts" to have access to the container, b. It is discoverable / extensible.

As far as not seeing it, I don't think it's painfully obvious, but it does illustrate one of my favorite features of MEF.

Glenn

Jan 27, 2010 at 6:04 PM

Just posted on this with a bit more elaboration and clean up of the code: http://codebetter.com/blogs/glenn.block/archive/2010/01/27/how-do-i-expose-configuration-information-through-mef.aspx