Archive for May, 2010

Custom Configuration for .NET (Part 4): Backgrounder on Configuration Sections

Posted in .NET Core on May 25th, 2010 by Richard – Comments Off on Custom Configuration for .NET (Part 4): Backgrounder on Configuration Sections

Configuration Sections

Updated 2010-06-22: Added link to part 5.

Introduction

Previous Parts of This Series.

There are two remaining approaches to providing structure that I have not yet covered: collections of elements and section groups. Element collections will be a topic for several future posts as there are a number of different approaches to creating element collections, and two of them make little sense without first covering configuration inheritance (see part 5).

Section groups are much simpler, and mostly a matter of declaring them.

Creating Section Groups

When custom sections are declared using the <configSections> element of a configuration file, one can declare both sections (with the <sections> element, using attributes to define the associated custom type and its name) and section groups.

To define a section group use the—unsurprisingly—<sectionsGroup> element. This needs a name and a type. Often the type will be System.Configuration.ConfigurationSectionGroup which adds no behaviour (but one can customise this, more below) and just allows programmatic access to its child sections and section groups (there is no specified limit to the depth of structure you can define—but don’t go overboard with deep structures which will become increasingly hard to understand and maintain).

For instance:

   <configuration>
    <configSections>
    <sectionGroup name="userSettings"
          type="System.Configuration.ConfigurationSectionGroup, System.Configuration, Version=4.0.0.0,
                                                                Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
      <section name="recentFiles"
           type="CustomConfiguration.RecentFilesConfigurationSection, MyAssembly"/>
    </sectionGroup>
    </configSections>

    <userSettings>
    <recentFiles showFiles="5" removeNoneExistant="true"/>
    </userSettings>

  </configuration>

Which defines a userSettings element to act as a container for (presumably) multiple sections describing many aspects of user behaviour.

Note, replace the strong name for System.Configuration with “System.Configuration.ConfigurationSectionGroup, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a” for .NET versions 2, 3 and 3.5.

Reading Section Groups and Contained Sections

In previous parts of this series the application code used Configuration.GetSection(‹name›) to get to its custom section (and the instance of Configuration was obtained with ConfigurationManager.OpenExeConfiguration). This works for configuration sections that are immediate children of the root <configuration> node.

To get a section defined with a group simple get the group, and open the section from within that group. For instance, based on the above sample configuration:

var config = ConfigurationManager.OpenExeConfiguration(…);
var userSettingsGroup = config.GetSectionGroup("userSettings");
var recentFiles = (RecentFilesConfigurationSection) userSettingsGroup.GetSection("recentFiles");

GetSectionGroup returns a ConfigurationSectionGroup instance which has GetSection and GetSectionGroup methods as Configuration has.

Configuration Section and Group Collection Properties

You might notice that the Configuration and ConfigurationSectionGroup also have properties Sections and SectionGroups which can be indexed by name. These give another approach to getting a section or section group:

  var myConfig = (MyConfigurationSection)config.GetSection("mine");
  var myConfig2 = (MyConfigurationSection)config.Sections["mine"];

are completely equivalent.

Custom Section Group Types

It is also possible to create your own custom configuration section group by deriving from ConfigurationSectionGroup, and a number of .NET Framework types do this. E.g. System.Xml.Serialization.Configuration.SerializationSectionGroup defined in the System.Xml assembly, which adds properties to type safely access contained sections (i.e. the properties contain the access into the Sections collection and cast the results to the correct type).

Previous Part of This Series on .NET Custom Configuration

  1. Demonstrating Custom Configuration Sections for .NET
  2. Demonstrating Custom Configuration for .NET (Part 2): Custom Elements
  3. Demonstrating Custom Configuration for .NET (Part 3): Validation

Demonstrating Custom Configuration for .NET (Part 3): Validation

Posted in .NET Core on May 23rd, 2010 by Richard – Comments Off on Demonstrating Custom Configuration for .NET (Part 3): Validation

Validating Values

Introduction

Previous Parts of This Series.

Parts 1 and 2 added structure and data, but this is often not enough, one needs valid data—the domain of the data for your configuration being narrower than the domain of the underlying type of the parsed values. Hence the need to extend the custom types representing the structure with more types and attributes to limit the ranges of those values.

Value Validation

Checking single values is the easiest part, albeit needing two custom types to be defined for each distinct kind of validation, but at least each case can be parameterised (i.e. if you need to check a date range, you only need two types, not four).

First: The Validator

The first type is derived from System.Configuration.ConfigurationValidatorBase. This will actually do the validation in two steps:

  1. Overriding CanValidate to confirm that the type can be validated. Return true to indicate you can, otherwise validation will fail. This override is usually used to confirm the type of the value matches the expectations of the next method.
  2. Override Validate to perform the validation. To indicate failure throw an exception (which will be wrapped by the configuration runtime, into a ConfigurationErrorsException.

NB. the validation needs to handle the default (before the configuration content is parsed) value. E.g. a DateTime value will be initially validated with a value of DateTime.MinValue before being called again with the value read from the configuration file. (For types which can be used with code attributes—see §17.1.3 of the C# specification, the default value is an optional parameter for the ConfigurastionProperty attribute. For other types, the code attribute based declaration of configuration values can be replaced with a more programmatic one, which I should cover later in this series.)

For instance to check that a DateTime value has a minimum year the following validation code will work (note the check to allow MinValue):

public class DateTimeValidator : ConfigurationValidatorBase {
    private DateTime lowerLimit;

    internal DateTimeValidator(DateTime lowerLimit) {
      this.lowerLimit = lowerLimit;
    }

    public override bool CanValidate(Type type) {
      return type.Equals(typeof(DateTime));
    }

    public override void Validate(object value) {
      Trace.Assert(value.GetType().Equals(typeof(DateTime)));
      DateTime val = (DateTime)value;

      // MinValue is used as a placeholder before the actual value is set
      if (val < lowerLimit && val != DateTime.MinValue) {
        throw new ArgumentException("Invalid DateTime value in configuration");
      }
    }
  }

Second: The Attribute

The second type is derived from ConfigurationValidatorAttribute (which itself derives from System.Attribute). This is used to (1) annotate the configuration property in the ConfigurationElement (or ConfigurationSection) type, and (2) be an object factory for the first, validation, type. Additional parameters can be passed from this attribute to the validator.

The key override is ValidatorInstance which needs to return an initialised instance of the validator class.

For example, to support the DateTimeValidator type, the following:

  [AttributeUsage(AttributeTargets.Property)]
  public class DateTimeValidatorAttribute : ConfigurationValidatorAttribute {
    public DateTimeValidatorAttribute() {
    }

    public override ConfigurationValidatorBase ValidatorInstance {
      get {
        return new DateTimeValidator(new DateTime(minYear, 1, 1));
      }
    }

    private int minYear = 1900;
    public int MinimumYear {
      get { return minYear; }
      set { minYear = value; }
    }
  }

Using Validation

The only change is to annotate the configuration property with the just defined attribute, passing any necessary additional parameters, e.g.:

  [ConfigurationProperty("startDate", IsRequired=true),
   DateTimeValidator(MinimumYear=2000)]
  public DateTime StartDate {
    get { return (DateTime)this["startDate"]; }
    set { this["startDate"] = value; }
  }

  [ConfigurationProperty("endDate", IsRequired=true),
   DateTimeValidator()]
  public DateTime EndDate {
    get { return (DateTime)this["endDate"]; }
    set { this["endDate"] = value; }
  }

Note the use of an explicit lower limit for StartDate but not for EndDate. (DateTime is not a type with literals available for attribute parameters, hence just using the year here.)

What Happens on Validation Failure?

Validation is performed when the configuration section is read, i.e. when Configuration.GetSection(‹name›) is called. Importantly this means that if configuration sections are not used, validation will not be performed, and no errors will be reported, however many invalid, missing or extra values there are. Also note that extra attributes and elements will be reported as errors, this behaviour can be modified by overriding ConfigurationElement’s OnDeserializeUnrecognizedAttribute or OnDeserializeUnrecognizedElement.

When an error, validation or otherwise (including malformed XML) occurs a ConfigurationErrorsException is thrown. If this is based on another exception being thrown internally (e.g. on malformed XML, a XmlException) then that original exception may be the ConfigurationErrorsException’s InnerException (sometimes this seems to be the base, other times not—I don’t see much consistency).

Any reporting of this to the user or logging is up to the application, this is not trivial as while the text of the message does include key information (like where the error was in the configuration file) it is not exactly in a user friendly format. But then configuration files are not targeted at (typical) end user direct editing.

Checking Values Together

There is no direct support for further validation after each individual value has been loaded and (given suitable attributes) validated. But ConfigurationElement does have the PostDeserialize method, which is called at the right time.

But the error message is not ideal, starting with the text: “An error occurred creating the configuration section handler for ‹section-name›”. But it does work.

See the code for AppConfigSection.cs for an example

The Complete Solution

DateTimeValidator.cs

namespace ConfigurationDemoPart3 {
  public class DateTimeValidator : ConfigurationValidatorBase {
    private DateTime lowerLimit;

    internal DateTimeValidator(DateTime lowerLimit) {
      this.lowerLimit = lowerLimit;
    }

    public override bool CanValidate(Type type) {
      return type.Equals(typeof(DateTime));
    }

    public override void Validate(object value) {
      Trace.Assert(value.GetType().Equals(typeof(DateTime)));
      DateTime val = (DateTime)value;

      // MinValue is used as a placeholder before the actual value is set
      if (val < lowerLimit && val != DateTime.MinValue) {
        throw new ArgumentException("Invalid DateTime value in configuration");
      }
    }
  }

  [AttributeUsage(AttributeTargets.Property)]
  public class DateTimeValidatorAttribute : ConfigurationValidatorAttribute {
    public DateTimeValidatorAttribute() {
    }

    public override ConfigurationValidatorBase ValidatorInstance {
      get {
        return new DateTimeValidator(new DateTime(minYear, 1, 1));
      }
    }

    private int minYear = 1900;
    public int MinimumYear {
      get { return minYear; }
      set { minYear = value; }
    }
  }
}

AppConfigSection.cs

namespace ConfigurationDemoPart3 {
  public class AppConfigSection : ConfigurationSection {

    [ConfigurationProperty("startDate", IsRequired=true),
     DateTimeValidator(MinimumYear=2000)]
    public DateTime StartDate {
      get { return (DateTime)this["startDate"]; }
      set { this["startDate"] = value; }
    }

    [ConfigurationProperty("endDate", IsRequired=true),
     DateTimeValidator()]
    public DateTime EndDate {
      get { return (DateTime)this["endDate"]; }
      set { this["endDate"] = value; }
    }

    protected override void PostDeserialize() {
      if (StartDate > EndDate) {
        throw new ArgumentException("EndDate must be after StartDate");
      }
      base.PostDeserialize();
    }
  }
}

Program.cs

namespace ConfigurationDemoPart3 {
  class Program {
    static void Main(string[] args) {
      var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
      ShowSection(config, "custom");
      ShowSection(config, "custom2");
    }

    private static void ShowSection(Configuration config, string secName) {
      try {
        var customSection = config.GetSection(secName) as AppConfigSection;

        Console.WriteLine("Config section \"{2}\": start = {0:d}, end = {1:d}", customSection.StartDate, customSection.EndDate, secName);

      } catch (ConfigurationErrorsException e) {
        var inner = e.InnerException;
        Console.WriteLine("Loading config section \"{0}\" failed: {1} (inner type {2})",
          secName, e.Message, inner == null ? "<None>" : inner.GetType().Name);
      }
    }
  }
}

app.config

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

  <custom startDate="2000-05-20" endDate="2010-05-22"/>
  <custom2 startDate="2010-05-20" endDate="2009-05-22"/>
</configuration>

Previous Part of This Series on .NET Custom Configuration

  1. Demonstrating Custom Configuration Sections for .NET
  2. Demonstrating Custom Configuration for .NET (Part 2): Custom Elements

Demonstrating Custom Configuration for .NET (Part 2): Custom Elements

Posted in .NET Core on May 21st, 2010 by Richard – Comments Off on Demonstrating Custom Configuration for .NET (Part 2): Custom Elements

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

Demonstrating Custom Configuration Sections for .NET (Part 1)

Posted in .NET Core, Development on May 17th, 2010 by Richard – 2 Comments

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>