Allowing Richer Structures by Adding Custom Elements

Introduction

Previous Parts of This Series.

Part 1 kept things really simple. But for non-trivial custom configuration just having XML attributes to work with starts to get very hard to edit in the file. Rather one wants to employ the richer hierarchical structures that XML allows: child elements. This turns out to be very easy.

Creating A Custom Element

To keep things simple, this example will just change part 1’s structure:

<custom date="2010-05-13" name="element name" />

into this:

<custom>
  <data date="2010-05-21" name="custom data name" />
</custom>

All this takes is deriving a new class from System.Configuration.ConfigurationElement with code attributes for the two configuration attributes, and then using this new type as the type of the code property in the ConfigurationSection derived class. See the source code below: DataConfigurationElement is the new custom element, and AppConfigSection is the updated custom section, now with just a single code property.

One limitation of this use of attributes is that text content is not supported (<element>text content</element>), custom elements still just use attributes, or child custom elements (there does not seem to be any depth limit). Unless one completely overrides the de-serialisation code that makes custom elements easy (by overriding the ConfigurationElement.DeserializeElement method), but then all the work—including parsing the attributes—would have to be manually coded using the passed XmlReader instance.

The Complete Solution

DataConfigurationElement.cs

namespace ConfigurationDemoPart2 {
  public class DataConfigurationElement : ConfigurationElement {
    [ConfigurationProperty("date", IsRequired=true)]
    public DateTime Date {
      get { return (DateTime) this["date"]; }
      set { this["date"] = value; }
    }
    [ConfigurationProperty("name", IsRequired=true)]
    public string Name {
      get { return (string)this["name"]; }
      set { this["name"] = value; }
    }
  }
}

AppConfigSection.cs

namespace ConfigurationDemoPart2 {
  public class AppConfigSection : ConfigurationSection {

    [ConfigurationProperty("data", IsRequired=true)]
    public DataConfigurationElement Data {
      get { return (DataConfigurationElement)this["data"]; }
      set { this["data"] = value; }
    }
  }
}

Program.cs

namespace ConfigurationDemoPart2 {
  class Program {
    static void Main(string[] args) {
      var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
      var customSection = config.GetSection("custom") as AppConfigSection;
      var dataElement = customSection.Data;

      Console.WriteLine("Config: date = {0}, name = {1}", dataElement.Date, dataElement.Name);
    }
  }
}

app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="custom"
             type="ConfigurationDemoPart2.AppConfigSection, ConfigurationDemoPart2"/>
  </configSections>

  <custom>
    <data date="2010-05-21" name="custom data name"/>
  </custom>
</configuration>

Previous Part of This Series on .NET Custom Configuration

  1. Demonstrating Custom Configuration Sections for .NET