A Simple Starting Point: Single Element with Attributes

Introduction

The <appSettings> element of a .NET .config with the ConfigurationSettings.AppSettings static collection to read them make adding a few simple string configuration settings to an application easy. But, limited to simple name-value pairs, this is (to put it nicely) limited. And then one compares it to the richness of structure (for example) the CLR and ASP.NET support. However all that capability to create rich hierarchical structures, including multiple types of collections with all values validated is also open to the developer.

It is just not obvious where to start (at least the documentation for .NET 4, e.g. here, is a big improvement over .NET 2''s).

Hence this series of posts which aim to be an aide-mémoire of various scenarios for different types of .config structures.

Keeping It Simple: A Single Element

How to add this

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

to the application (or ASP.NET) configuration file?

Note: both XML and C# code have attributes, so I’ll write configuration attribute for XML attributes on the config file, and code attribute for those in the source code.

Setting Up The Project

Any kind of .NET project can be used, I’m using C# in a console project here (so I can just focus on configuration). This will also work against a .NET 2, 3, 3.5 or 4 target. The sample here is a console application called ConfigurationDemoPart1, so that will also be the namespace.

Start by adding a reference to the System.Configuration assembly.

Then add a new public class, for this sample called AppConfigSection. Derive from System.Configuration.ConfigurationSection. This base is needed for any custom type that is referenced by the <configSections> to define the configuration file section being created.

Defining Attributes on the Configuration Element

To create each configuration attribute to appear on the custom element add a property to the section class.

Each property needs a System.Configuration.ConfigurationPropertyAttribute code attribute with the name of the configuration attribute, and must use the default dictionary inherited from ConfigurationSection as a backing store: do not use automatic properties or a local field. The key name used in the dictionary must match the name in the ConfigurationProperty.

This is easier to show by example than explain:

  [ConfigurationProperty("date")]
  public DateTime Date {
      get { return (DateTime)this["date"]; }
      set { this["date"] = value; }
  }

Accessing the Custom Configuration

Without this the whole effort would be pointless, why write configuration file content if you are not going to use it?

Create an instance of System.Configuration.Configuration by using the OpenExeConfiguration method of System.Configuration.ConfigurationManager. Pass System.Configuration.ConfigurationUserLevel.None to just access the application configuration file (or web.config). Per-user configuration files, and directly loading a specific file (i.e. not the default name) is also possible, but that’s something for later.

Get to the custom configuration with GetSection(name) and casting to the custom configuration type. The properties will have the values from the configuration file.

You can also set the value of the properties. While this will change the value subsequent reads of the properties returns it will not change the configuration file. It is possible to save changes, but that’s a topic for a later post.

Adding To The Configuration File

As well as adding the new custom element, the configuration system needs to be told about the new element. Do this with the <configSections> element, giving the new section a name (which you also need to pass, in code, to GetSection) and a type:

<configSections>
  <section name="custom"
           type="ConfigurationDemoPart1.AppConfigSection, ConfigurationDemoPart1" />
</configSections>

Possible Errors

A null reference on accessing a property in code to get a configuration attribute’s value probably means one of the code attribute on the custom configuration property, the index into the values dictionary or the configuration attribute is spelt differently. (This took ages to work out the first time it happened to me, now it is always the first thing to check.)

The Complete Solution

AppConfigSection.cs

This defines the custom type

namespace ConfigurationDemoPart1 {
  public class AppConfigSection : ConfigurationSection {
    [ConfigurationProperty("date")]
    public DateTime Date {
      get { return (DateTime)this["date"]; }
      set { this["date"] = value; }
    }

    [ConfigurationProperty("name")]
    public string Name {
      get { return (String)this["name"]; }
      set { this["name"] = value; }
    }
  }
}

Program.cs

Minimal demonstration code using the custom configuration

namespace ConfigurationDemoPart1 {
  class Program {
    static void Main(string[] args) {
      var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
      var customSection = config.GetSection("custom") as AppConfigSection;
      Console.WriteLine("Config: date = {0}, name = {1}", customSection.Date, customSection.Name);
    }
  }
}

app.config

Visual Studio will convert this to ConfigurationDemoPart1.exe.config when the project is built.

<configuration>
  <configSections>
    <section name="custom"
             type="ConfigurationDemoPart1.AppConfigSection, ConfigurationDemoPart1" />
  </configSections>

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