tsJensen

A quest for software excellence...

ConfigurationSection Lesson on ConfigurationErrorsException

I was playing around with some old code in which I had a custom ConfigurationSection (see code below). I had an attribute in the configuration section that I wanted to change from an Int32 to a Double. Seems a simple change. Just a little refactoring of the ConfigurationElement class changing the "int" declaration to "double" and it compiles. But kablam! It does not run but throws a nasty System.Configuration.ConfigurationErrorsException with this explanation:

The default value for the property 'size' has different type than the one of the property itself.

Huh? Turns out the type inference on the DefaultValue property in the ConfigurationProperty attribute is rather picky. Here's what I had originally:

[ConfigurationProperty("size", DefaultValue = 1, IsKey = false, IsRequired = true)]
public int Size
{
    get
    { return (int)this["size"]; }
    set
    { this["size"] = value.ToString(); }
}

And here's what I changed it to thinking it would run just fine:

[ConfigurationProperty("size", DefaultValue = 1, IsKey = false, IsRequired = true)]
public double Size
{
    get
    { return (double)this["size"]; }
    set
    { this["size"] = value.ToString(); }
}

Just because the above code compiled, does not mean it will work correctly. So here's what really worked:

[ConfigurationProperty("size", DefaultValue = 1.0, IsKey = false, IsRequired = true)]
public double Size
{
    get
    { return (double)this["size"]; }
    set
    { this["size"] = value.ToString(); }
}

Small but important lesson to learn. Here's the whole code and following it the snippet from the config file in case this is the first time you've written a custom config handler.

using System;
using System.Collections;
using System.Text;
using System.Configuration;
using System.Xml;

namespace MyConfigBox
{
    public class SizeLimitConfigurationSection : ConfigurationSection
    {
        [ConfigurationProperty("hostSize")]
        public HostSizeConfigCollection HostSize
        {
            get
            {
                return ((HostSizeConfigCollection)(base["hostSize"]));
            }
        }
    }

    [ConfigurationCollectionAttribute(typeof(HostSizeConfigElement))]
    public class HostSizeConfigCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new HostSizeConfigElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((HostSizeConfigElement)(element)).Name;
        }

        public void Add(HostSizeConfigElement element)
        {
            this.BaseAdd(element);
        }

        public void Remove(string key)
        {
            this.BaseRemove(key);
        }

        public void Clear()
        {
            this.BaseClear();
        }

        public HostSizeConfigElement this[int idx]
        {
            get { return (HostSizeConfigElement)this[idx]; }
        }
    }

    public class HostSizeConfigElement : ConfigurationElement
    {
        public HostSizeConfigElement() { }
        public HostSizeConfigElement(string name, double size)
        {
            this.Name = name;
            this.Size = size;
        }

        [ConfigurationProperty("name", DefaultValue = "_", IsKey=true, IsRequired = true)]
        [StringValidator(InvalidCharacters = "~!@#$%^&()[]{}/;'\"|\\", MinLength = 1, MaxLength = 260)]
        public string Name
        {
            get
            { return (string)this["name"]; }
            set
            { this["name"] = value; }
        }

        [ConfigurationProperty("size", DefaultValue = 1.0, IsKey = false, IsRequired = true)]
        public double Size
        {
            get
            { return (double)this["size"]; }
            set
            { this["size"] = value.ToString(); }
        }
    }
}

And now here's the config file (some items removed to clean it up a bit and just show the relevant parts):

<?xml version="1.0"?>
<configuration>
    <configSections>
        <section name="sizeLimits" type="MyConfigBox.SizeLimitConfigurationSection, MyConfigBox" />
    </configSections>
    <appSettings/>
    <connectionStrings/>

    <sizeLimits>
        <hostSize>
            <add name="netbrick.net" size="17.5" />
            <add name="duovia.com" size="42.4" />
        </hostSize>
    </sizeLimits>

</configuration>

And here's an example of how to use the custom config handler in code:

SizeLimitConfigurationSection config =
   (SizeLimitConfigurationSection)(System.Configuration.ConfigurationManager.GetSection("sizeLimits"));
//now use config to get at the items in the hostSize collection of HostSizeConfigElement objects

So watch those attribute values because sometimes the compiler won't.