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?

This entry was posted in MEF, silverlight, WPF. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Abog

    I’m trying to use this approach to export a third party control. The problem I’m facing is that this control is a visual control and it is placed in the main page. So if I expose it through some property in the Main page I cannot call SatisfyImports on that page anymore since it has exports in it.
    I guess I could move that property to some helper class, export it and then import to the Main page and to every other place needed but then i will have to add it in a code behind and loose the Blendability (whatever it is called). So I’d like the control to stay on the Main page (added in Xaml) but then I cannot think a way to export it.
    I hope i made it clear. Any suggestions are welcome

  • http://codebetter.com/members/gblock/default.aspx Glenn Block
  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    I see what you are saying. There are several ways you could address it. In keeping with the spirit of keeping the testing simple, I think the following approach would work. Export a Func rather than T. This gives the opportunity for the value to change everytime the func is accessed. MEF also supports method exports, which would work great for this scenario.

    For example:

    public class RecentlyUsedTrackerConfigiuration {

    [Export("RecentlyUsedTracker.File")]
    public string GetRecentlFile() {
    return _provider.GetValue(“recentlyUsedTracker.file”);
    }
    }

    Above the export is a Func rather than a string. The importer will import Func

    [Export]
    public class RecentlyUsedTracker {

    [Import("RecentlyUsedTracker.File")]
    public Func File {get;set;}

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

    As you can see above, method exports are imported.

    The next question might be what if one wants to observe that the object changes? If the importer needed to monitor changes, you could export an IObservable instead. In that case you wouldn’t use a method export, but would revert back to a property.

    Note: You could also have used a property export above instead of a method. The method export syntax is more readable and intuitive, which is why I opted for it.

    Seem reasonable?

  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    @Mike

    You could definitely do that. I would opt for using a custom ExportProvider over a catalog though. The reason is because ExportProviders are designed to just return values.

    Catalogs are ideal when you are representing parts which have imports that need to be satisifed by the container. In the case of config, you are normally dealing with values pulled from somewhere (app.config, a database, etc) thus EPs are ideal.

  • natl

    just because some of the configuration can be changed by the user. If you define a singleton configuration object, you can change it at anytime.. configuration injection via property import won’t help in this case…

  • Mike

    Could you not create a catalog that exposed automatically exposed the values from the appSettings section of the config file?

  • http://codebetter.com/members/gblock/default.aspx Glenn Block

    Good question. If you change the part to have a creation policy non-shared then each pull on the config will create a new part instance rather than sharing the same. If that were the case then I would advise putting the logic in the getter rather than the ctor as it would be expensive to pull all values.

    If what you are asking about is having the config be recomposable, that is a whole other ball of wax and will require a more sophisticated route.

    Do you need that?

  • natl

    Thanks for answering that. What if the configuration can be changed dynamically?