June 2012

Volume 27 Number 06

CLR - An Attribute-Free Approach to Configuring MEF

By Alok Shriram | June 2012

The Managed Extensibility Framework (MEF) was designed to give Microsoft .NET Framework developers an easy way to build loosely coupled applications. The primary focus of MEF in version 1 was extensibility, such that an application developer could expose certain extension points to third-party developers, and the third-party developers could build add-ons or extensions to those components. The Visual Studio plug-in model for extending Visual Studio itself is an excellent use case of this, which you can read about on the MSDN Library page, “Developing Visual Studio Extensions” (bit.ly/IkJQsZ). This method of exposing extension points and defining plug-ins uses what’s known as an attributed programming model, in which a developer can decorate properties, classes and even methods with attributes to advertise either a requirement for a dependency of a specific type or the ability to satisfy the dependency of a specific type.

Despite the fact that attributes are very useful for extensibility scenarios where the type system is open, they’re overkill for closed-type systems that are known at build time. Some fundamental issues with the attributed programming model are:

  1. Configuration for many similar parts involves a lot of needless repetition; this is a violation of the Don’t Repeat Yourself (DRY) principle, and in practice can lead to human error and source files that are more difficult to read.
  2. Authoring an extension or part in the .NET Framework 4 means dependence on MEF assemblies, which ties the developer to a specific dependency injection (DI) framework.
  3. Parts that were not designed with MEF in mind need to have the attributes added to them in order to be appropriately identified in applications. This can serve as a significant barrier to adoption.

The .NET Framework 4.5 provides a way to centralize configuration so that a set of rules can be written on how extension points and components are created and composed. This is achieved using a new class called RegistrationBuilder (bit.ly/HsCLrG), which can be found in the System.ComponentModel.Composition.Registration namespace. In this article I’ll first consider some reasons for using a system such as MEF. If you’re an MEF veteran, you might be able to skip this part. Next, I’ll don the role of a developer who’s been given a set of requirements and create a simple console application using the MEF attributed programming model. I’ll then convert this application to the convention-based model, showing how to implement some typical scenarios using RegistrationBuilder. Finally, I’ll discuss how convention-driven configuration is already being incorporated into application models and how it makes using MEF and DI principles out of the box a trivial task.

Background

As software projects grow in size and scale, maintainability, extensibility and testability become key concerns. As software projects mature, components might need to be replaced or refined. As projects grow in scope, requirements often change or get added. The ability to add functionality to a large project in a simple manner is extremely critical to the evolution of that product. Moreover, with change being the norm during most software cycles, the ability to rapidly test components that are a part of a software product, independently of other components, is crucial—especially in environments where dependent components are developed in parallel.

With these driving forces, the notion of DI has become popular in large-scale software development projects. The idea behind DI is to develop components that advertise the dependencies they need without actually instantiating them, as well as the dependencies they satisfy, and the dependency-injection framework will figure out and “inject” the correct instances of the dependencies into the component. “Dependency Injection,” from the September 2005 issue of MSDN Magazine (msdn.microsoft.com/magazine/cc163739), is an excellent resource if you need more background information.

The Scenario

Now let’s get to the scenario I described earlier: I’m a developer looking at a specification I’ve been given. At a high level, the goal of the solution that I’m going to implement is to provide a weather forecast to a user based on his ZIP code. Here are the required steps:

  1. The application requests a ZIP code from the user.
  2. The user enters a valid ZIP code.
  3. The application contacts an Internet weather service provider for forecast information.
  4. The application presents formatted forecast information to the user.

From a requirements perspective, it’s clear there are some unknowns at this point, or aspects that have the potential to change later on in the cycle. For example, I don’t yet know which weather service provider I’m going to use, or what method I’ll employ for getting data from the provider. So to start designing this application, I’ll break the product into a couple of discrete functional units: WeatherServiceView, IWeatherServiceProvider and IDataSource. The code for each of these classes is shown in Figure 1, Figure 2 and Figure 3, respectively.

Figure 1 WeatherServiceView—the Results Display Class

[Export]
public class WeatherServiceView
{
  private IWeatherServiceProvider _provider;
  [ImportingConstructor]
  public WeatherServiceView(IWeatherServiceProvider providers)
  {
    _providers = providers;
  }
  public void GetWeatherForecast(int zipCode)
  {
    var result=_provider.GetWeatherForecast(zipCode);
      // Some display logic
  }
}

Figure 2 IWeatherServiceProvider (WeatherUnderground) Data Parsing Service

[Export(typeof(IWeatherServiceProvider))]
class WeatherUndergroundServiceProvider:IWeatherServiceProvider
{  private IDataSource _source;
  [ImportingConstructor]
  public WeatherUndergroundServiceProvider(IDataSource source)
  {
    _source = source;
  }
  public string GetWeatherForecast(int zipCode)
  {
    string val = _source.GetData(GetResourcePath(zipCode));
      // Some parsing logic here
    return result;
  }
  private string GetResourcePath(int zipCode)
  {
    // Some logic to get the resource location
  }
}

Figure 3 IDataSource (WeatherFileSource)

[Export(typeof(IDataSource))]
class WeatherFileSource :IDataSource
{
  public string GetData(string resourceLocation)
  {
    Console.WriteLine("Opened ----> File Weather Source ");
    StringBuilder builder = new StringBuilder();
    using (var reader = new StreamReader(resourceLocation))
    {
      string line;
      while((line=reader.ReadLine())!=null)
      {
        builder.Append(line);
      }
    }
    return builder.ToString();
  }
}

Finally, in order to create this hierarchy of parts, I need to use a Catalog from which I can discover all the parts in the application, and then use the CompositionContainer to get an instance of WeatherServiceView, on which I can then operate, like so:

class Program
{
  static void Main(string[] args)
  {
    AssemblyCatalog cat = 
      new AssemblyCatalog(typeof(Program).Assembly);
    CompositionContainer container = 
      new CompositionContainer(cat);           
    WeatherServiceView forecaster =
      container.GetExportedValue<WeatherServiceView>();
    // Accept a ZIP code and call the viewer
    forecaster.GetWeatherForecast(zipCode);
  }
}

All of the code I’ve presented so far is fairly basic MEF semantics; if you’re unclear about how any of this works, please take a look at the “Managed Extensibility Framework Overview” MSDN Library page at bit.ly/JLJl8y, which details the MEF attributed programming model.

Convention-Driven Configuration

Now that I have the attributed version of my code working, I want to demonstrate how you convert these pieces of code to the convention-driven model using RegistrationBuilder. Let’s start by removing all the classes to which MEF attributes were added. As an example, take a look at the code fragment in Figure 4, modified from the WeatherUnderground data parsing service shown in Figure 2.

Figure 4 WeatherUnderground Data Parsing Class Converted into Simple C# Class

class WeatherUndergroundServiceProvider:IWeatherServiceProvider
{
  private IDataSource _source;
  public WeatherUndergroundServiceProvider(IDataSource source)
  {
    _source = source;
  }
  public string GetWeatherForecast(int zipCode)
  {
    string val = _source.GetData(GetResourcePath(zipCode));
    // Some parsing logic here
    return result;
  }
      ...
}

The code in Figure 1 and Figure 3 will change in the same way as in Figure 4.

Next, I use RegistrationBuilder to define certain conventions in order to express what we specified using the attributes. Figure 5 shows the code that does this.

Figure 5 Setting up Conventions

RegistrationBuilder builder = new RegistrationBuilder();
    builder.ForType<WeatherServiceView>()
      .Export()
      .SelectConstructor(cinfos => cinfos[0]);
    builder.ForTypesDerivedFrom<IWeatherServiceProvider>()
      .Export<IWeatherServiceProvider>()
      .SelectConstructor(cinfo => cinfo[0]);
    builder.ForTypesDerivedFrom<IDataSource>()
      .Export<IDataSource>();

Each declaration of a rule has two distinct parts. One part identifies a class or a set of classes to be operated on; the other specifies the attributes, metadata and sharing policies to apply to the selected classes, properties of the classes or constructors of the classes. Thus you can see that lines 2, 5 and 8 start the three rules I’m defining, and the first part of each rule identifies the type on which the rest of the rule is going to be applied. In line 5, for instance, I want to apply a convention for all types that are derived from IWeatherServiceProvider.

Now let’s take a look at the rules and map them back to the original attributed code in Figures 1, 2 and 3. WeatherFileSource (Figure 3) was simply exported as IDataSource. In Figure 5, the rule in lines 8 and 9 specifies picking all the types that are derived from IDataSource and exporting them as contracts of IDataSource. In Figure 2, observe that the code exports type IWeatherService­Provider and requires an import of IDataSource in its constructor, which was decorated with an ImportingConstructor attribute. The corresponding rule for this in Figure 5 is specified in lines 5, 6 and 7. The added piece here is the method SelectConstructor, which accepts a Func<ConstructorInfo[], ConstructorInfo>. This gives me a way to specify a constructor. You can specify a convention, such as the constructor with the smallest or largest number of arguments will always be the ImportingConstructor. In my example, because I have only one constructor, I can use the trivial case of picking the first and only constructor. For the code in Figure 1, the rule in Figure 5 is specified in lines 2, 3 and 4, and is similar to the one just discussed.

With the rules established, I need to apply them to the types present in the application. To do this, all catalogs now have an overload that accepts a RegistrationBuilder as a parameter. So you’d modify the previous CompositionContainer code  as shown in Figure 6.

Figure 6 Consuming the Conventions

class Program
{
  static void Main(string[] args)
  {
    // Put the code to build the RegistrationBuilder here
    AssemblyCatalog cat = 
      new AssemblyCatalog(typeof(Program).Assembly,builder);
    CompositionContainer container = new CompositionContainer(cat);           
    WeatherServiceView forecaster =
      container.GetExportedValue<WeatherServiceView>();
    // Accept a ZIP code and call the viewer
    forecaster.GetWeatherForecast(zipCode);
  }
}

Collections

Now I’m done and my simple MEF application is up and running without attributes. If only life were so simple! I’m now told my app needs to be able to support more than one weather service and that it needs to display forecasts from all the weather services. Luckily, because I used MEF, I don’t have to panic. This is simply a scenario with multiple implementers of an interface, and I need to iterate through them. My example now has more than one implementation of IWeatherServiceProvider and I want to display the results from all of these weather engines. Let’s take a look at the changes I need to make, as shown in Figure 7.

Figure 7 Enabling Multiple IWeatherServiceProviders

public class WeatherServiceView
{
  private IEnumerable<IWeatherServiceProvider> _providers;
  public WeatherServiceView(IEnumerable<IWeatherServiceProvider> providers)
  {
    _providers = providers;
  }
  public void GetWeatherForecast(int zipCode)
  {
    foreach (var _provider in _providers)
    {
      Console.WriteLine("Weather Forecast");
      Console.WriteLine(_provider.GetWeatherForecast(zipCode));
    }
    }
}

That’s it! I changed the WeatherServiceView class to accept one or more IWeatherServiceProvider implementations and in the logic section I iterated through that collection. The conventions I established earlier will now capture all the implementations of IWeatherServiceProvider and export them.  However, something seems to be missing in my convention: At no point did I have to add an ImportMany attribute or equivalent convention when I was configuring the WeatherServiceView. This is a little bit of RegistrationBuilder magic in which it figures out that if your parameter has an IEnumerable<T> on it, it must be an ImportMany, without you having to explicitly specify it. So using MEF made the job of extending my application simple, and, by using RegistrationBuilder, as long as the new version implemented IWeaterServiceProvider, I didn’t have to do anything to make it work with my app. Beautiful!

Metadata

Another really useful feature in MEF is the ability to add metadata to parts. Let’s assume for the sake of the discussion that in the example we’ve been looking at, the value returned by the GetResourcePath method (shown in Figure 2) is governed by the concrete type of IDataSource and IWeatherServiceProvider being used. So I define a naming convention specifying that a resource will be named as an underscore-delimited (“_”) combination of the weather service provider and the data source. With this convention, the Weather Underground services provider with a Web data source will have the name WeatherUnderground_Web_ResourceString. The code for this is shown in Figure 8.

Figure 8 Resource Description Definition

public class ResourceInformation
{
  public string Google_Web_ResourceString
  {
    get { return "https://www.google.com/ig/api?weather="; }
  }
  public string Google_File_ResourceString
  {
    get { return @".\GoogleWeather.txt"; }
  }
  public string WeatherUnderground_Web_ResourceString
  {
    get { return
      "https://api.wunderground.com/api/96863c0d67baa805/conditions/q/"; }
  }
}

Using this naming convention I can now create a property in the WeatherUnderground and Google weather service providers that will import all these resource strings and, based on their current configurations, pick the appropriate one. Let’s look first at how to write the RegistrationBuilder rule to configure the ResourceInformation as an Export (see Figure 9).

Figure 9 The Rule for Exporting Properties and Adding Metadata

builder.ForType<ResourceInformation>()
       .ExportProperties(pinfo => 
       pinfo.Name.Contains("ResourceString"),
    (pinfo, eb) =>
      {
        eb.AsContractName("ResourceInfo");
        string[] arr = pinfo.Name.Split(new char[] { '_' },
          StringSplitOptions.RemoveEmptyEntries);
        eb.AddMetadata("ResourceAffiliation", arr[0]);
        eb.AddMetadata("ResourceLocation", arr[1]);
     });

Line 1 simply identifies the class. Line 2 defines a predicate that picks all the properties in this class that contain ResourceString, which is what my convention dictated. The last argument of ExportProperties is an Action<PropertyInfo,ExportBuilder>, in which I specify that I want to export all properties that match the predicate specified in line 2 as a named contract called ResourceInfo, and want to add metadata based on parsing the name of that property using the keys ResourceAffiliation and ResourceLocation. On the consuming side, I now need to add a property to all implementations of IWeatherServiceProvider, like this:

public IEnumerable<Lazy<string, IServiceDescription>> WeatherDataSources { get; set; }

And then add the following interface to use strongly typed metadata:

public interface IServiceDescription
  {
    string ResourceAffiliation { get; }
    string ResourceLocation { get; }   
  }

To learn more about metadata and strongly typed metatdata, you can read the helpful tutorial at bit.ly/HAOwwW.

Now let’s add a rule in RegistrationBuilder to import all parts that have the contract name ResourceInfo. To do this, I’ll take the existing rule from Figure 5 (lines 5-7) and add the following clause:

builder.ForTypesDerivedFrom<IWeatherServiceProvider>()
       .Export<IWeatherServiceProvider>()
       .SelectConstructor(cinfo => cinfo[0]);
       .ImportProperties<string>(pinfo => true,
                                (pinfo, ib) =>
                                 ib.AsContractName("ResourceInfo"))

Lines 8 and 9 now specify that all types derived from IWeather­ServiceProvider should have an Import applied on all properties of type string, and the import should be made on the contract name ResourceInfo. When this rule runs, the previously added property becomes an Import for all contracts with the name ResourceInfo. I can then query the enumeration to filter out the correct resource string, based on metadata.

The End of Attributes?

If you consider the samples I’ve discussed, you’ll see that we don’t really seem to require attributes anymore. Anything you could do with the attributed programming model can now be achieved using the convention-based model. I mentioned some common use cases where RegistrationBuilder can help, and Nicholas Blumhardt’s great write-up on RegistrationBuilder at bit.ly/tVQA1J can give you more information. However, attributes can still play a key role in a convention-driven MEF world. One significant issue with conventions is that they’re great only as long as they’re followed. As soon as an exception to the rule occurs, the overhead of maintaining the conventions can get prohibitively expensive, but attributes can help with overriding conventions. Let’s assume that a new resource was added in the ResourceInformation class, but its name did not adhere to the convention, as shown in Figure 10.  

Figure 10 Overriding Conventions Using Attributes

public class ResourceInformation
{
  public string Google_Web_ResourceString
  {
    get { return "https://www.google.com/ig/api?weather="; }
  }
  public string Google_File_ResourceString
  {
    get  { return @".\GoogleWeather.txt"; }
  }
  public string WeatherUnderground_Web_ResourceString
  {
    get { return "https://api.wunderground.com/api/96863c0d67baa805/conditions/q/"; }
  }
  [Export("ResourceInfo")]
  [ExportMetadata("ResourceAffiliation", "WeatherUnderground")]
  [ExportMetadata("ResourceLocation", "File")]
  public string WunderGround_File_ResourceString
  {
    get { return @".\Wunder.txt"; }
  }
}

In Figure 10, you can see that the first part of the convention is incorrect, according to the naming specification. However, by going in and explicitly adding a contract name and metadata that is correct, you can override or add to the parts discovered by RegistrationBuilder, thus making MEF attributes an effective tool for specifying exceptions to the conventions defined by RegistrationBuilder.

Seamless Development

In this article I’ve looked at convention-driven configuration, a new feature in MEF exposed in the RegistrationBuilder class that greatly streamlines development associated with MEF. You’ll find beta versions of these libraries at mef.codeplex.com. If you don’t yet have the .NET Framework 4.5, you can go to the CodePlex site and download the bits. 

Ironically, RegistrationBuilder can make your day-to-day development activities revolve less around MEF, and your use of MEF in your projects extremely seamless. A great example of this is the integration package built into Model-View-Controller (MVC) for MEF, which you can read about on the BCL Team Blog at bit.ly/ysWbdL. The short version is that you can download a package into your MVC application, and this sets up your project to use MEF. The experience is that whatever code you have “just works” and, as you start following the convention specified, you get the benefits of using MEF in your application without having to write a line of MEF code yourself. You can find out more about this on the BCL Team blog at bit.ly/ukksfe.


Alok Shriram is a program manager for the Microsoft .NET Framework team at Microsoft, where he works in the Base Class Libraries team. Before that he worked as a developer on the Office Live team, which later became the Office 365 team. After a stint of grad school at the University of North Carolina, Chapel Hill, he’s currently based in Seattle. In his spare time he likes to explore all that the Pacific Northwest has to offer with his wife Mangal. He can be found lurking on the MEF CodePlex site, on Twitter at twitter.com/alokshriram and occasionally posting on the .NET blog.

Thanks to the following technical experts for reviewing this article: Glenn Block, Nicholas Blumhardt and Immo Landwerth