Tuesday, December 22, 2009

Using MEF and Custom Configuration Sections

Up to this point, I've dove into some of the core fundamentals of using the Managed Extensibility Framework.  Starting with this post, we're going to dive into how to apply it under certain circumstances.  Some of the scenarios in the upcoming posts will focus on using MEF with different .Net technologies, applications that use MEF, or tricks in .Net that can assist when working with MEF.  In this post, we'll start with this last category of scenarios by looking at how to utilize a custom configuration section defined in an assembly used as a MEF part and not located in the same directory as the executable.  Using this method, it will allow even greater power to your application and freedom to the parts consumed by such.

Setting the Stage

For this post, we'll look at a simple code base we worked on earlier in the series which imported MEF parts from a separate assembly.  Let's extend the code base by defining our simple custom configuration section from within the same assembly that define our external part.  For simplicity sake, let's call the custom configuration section MEFCustomConfigSection.  Below is the code for our custom configuration section:

   1:  public class MEFCustomConfigSection : ConfigurationSection
   2:  {
   3:      private static MEFCustomConfigSection settings = ConfigurationManager.GetSection("MEFCustomConfigSection") as MEFCustomConfigSection;
   4:   
   5:   
   6:      public static MEFCustomConfigSection Settings
   7:      {
   8:          get { return settings; }
   9:      }
  10:   
  11:      [ConfigurationProperty("message", IsRequired = true)]
  12:      public string Message
  13:      {
  14:          get { return this["message"].ToString(); }
  15:          set { this["message"] = value; }
  16:      }
  17:  }

This configuration section is very simple in that it only defines one property/attribute called Message.  This property will contain a string value in which we'll be displaying on the screen when the application consumes our part.

Consuming Our Section

Now that our configuration section has been defining, let's go ahead and create the code to consume such.  In previous examples, each of our parts had a property called HelpText that is used by the consuming application to display command line help documentation for each part.  What we will do with this new part is using our configuration section's Message property to define what the value of the HelpText property should be.  Thankfully, the code for this is extremely simple as shown below:

   1:  [Export("command", typeof(IHelp))]
   2:  public class CustomCommand : IHelp
   3:  {
   4:      public string CommandName
   5:      {
   6:          get { return "Custom"; }
   7:      }
   8:   
   9:      public string HelpText
  10:      {
  11:          get { return MEFCustomConfigSection.Settings.Message; }
  12:      }
  13:  }

Setting Up the Configuration File

Now that we have the custom configuration section created and code that needs it, we're now ready to update our consuming application's App.config file.

   1:  <configuration>
   2:      <configSections>
   3:          <section name="MEFCustomConfigSection" 
   4:                   type="MEFExample7.Commands.MEFCustomConfigSection, MEFExample7.Commands" />
   5:      </configSections>
   6:   
   7:      <MEFCustomConfigSection message="Hello From a Custom Section" />
   8:   
   9:      <runtime>
  10:          <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  11:              <probing privatePath="Plug-ins;" />
  12:          </assemblyBinding>
  13:      </runtime>    
  14:  </configuration>

Setting up the custom configuration section is the same as any other configuration section that would need to be setup.  We first define our section within the <configSections> node and then we're able to insert our section; however, what's that last section?

The <runtime> section in the above snippet is needed to tell .Net to scan the Plug-ins subdirectory for the assembly that contains our configuration section.  There are two ways of doing this.  The example above uses the <probing> node which is great for little examples like this; however, can lead to performance issues since it will scan all assemblies in the paths that are provided.  Another issue with the <probing> node is that it doesn't provide a way for targeting specific, strongly named assemblies nor specific assembly versions.  If you need stronger control of what assemblies gets loaded, you can use the <codeBase> node instead.

For more information on using the <probing> and <codeBase> nodes for loading assemblies this way, feel free to check out the following links:

NOTE: One final item I need to mention about using <probing>, it can only look in subdirectories of the location the executable is in.  So if the assembly is in /bin, your path to probe for assemblies cannot be in a parent directory.

Once the configuration section is setup, and the Plug-ins directory is a child directory of the location of the executable, our new command is now displayed when the application runs.

Summary

In this post, we briefly looked at some things to be aware of when defining a custom configuration section from with in an assembly used as an external MEF part.  In future posts, we'll be looking at other scenarios on using MEF including consuming parts written in F# by a C# application as well as take a look at an open source project that will soon be releasing a new version that uses MEF.

Resources

kick it on DotNetKicks.comShout it

1 comment: