Monday, July 27, 2009

Storing custom settings in configuration files (app.config/web.config)

How do you go about storing custom settings in a configuration file?

If all you want to do is to store a name value pair, then use “<appSettings>” section.
eg:

<appSettings>
   <add key="keyName" value="associatedValue"/>
</appSettings>

But what do you do if you wish to store more than one value per key. In this case you need to create a custom configuration section.

Lets first look at an example of such a section:

<EFTConfiguration>
  <Settings>
    <add Name="setting1" Value1="Some Value" Value2="More Values" Value3="Some More Values" />
    <add Name="setting2" Value1="Some Value 2" Value2="More Values 2" Value3="Some More Values 2" />
  </Settings>
</EFTConfiguration>

So how do you do this?

  1. Add some code:
    1. Specifically you need to add a class that derives from “ConfigurationSection”, which will represent the “EFTConfiguration” section in the app.config file shown above.
    2. A class that derives from “ConfigurationElementCollection” which will represent the collection of “Settings” object in the above config file.
    3. A class that derives from “ConfigurationElement” which will represent each of the settings line that appears within the “Settings” element.
  2. Add the properly formatted configuration section (as shown above) to the config file.
  3. Add a reference to the class that implements “ConfigurationSection” to the app.config file. (In the <configuration><configSections> section).

We will approach the code sections in reverse (as that defines the object dependencies of each class we need to implement:

Step 1.3: Create a class that inherits from the ConfigurationElement class.

This class stores the property values for each of the individual settings shown above. You will find that each property (Name, Value1, Value2, Value3) are implemented as properties. In addition the meta data defines whether the property is required or not and which one of the properties acts as the key (in this case it is Name).

public class EFTSettingsElement : ConfigurationElement
{
    [ConfigurationProperty("Name", DefaultValue = null, IsKey = true, IsRequired = true)]
    public String Name
    {
        get { return (String)this["Name"]; }
        set { this["Name"] = value; }
    }

    [ConfigurationProperty("Value1", DefaultValue = null, IsKey = false, IsRequired = true)]
    public String Value1
    {
        get { return (String)this["Value1"]; }
        set { this["Value1"] = value; }
    }

    [ConfigurationProperty("Value2", DefaultValue = null, IsKey = false, IsRequired = true)]
    public String Value2
    {
        get { return (String)this["Value2"]; }
        set { this["Value2"] = value; }
    }

    [ConfigurationProperty("Value3", DefaultValue = null, IsKey = false, IsRequired = false)]
    public String Value3
    {
        get { return (String)this["Value3"]; }
        set { this["Value3"] = value; }
    }
}

Step 1.2: Create a class that derives from the ConfigurationElementCollection class

This class acts as a collection that will store all the settings objects defined as EFTSettingsElements. The main thing here is that GetElementKey has been overriden and we have added some indexer properties.

[ConfigurationCollection(typeof(EFTSettingsElement))]
public class EFTSettingsCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new EFTSettingsElement();
    }

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

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

    public EFTSettingsElement this[string name]
    {
        get
        {
            return (EFTSettingsElement)BaseGet(name);
        }
    }
}

Step 1.1: Create a class that derives from ConfigurationSection class

Because the configuration section is called “EFTConfiguration” in my app.config, I am going to name the class “EFTConfigurationSection”. The name of the element that will contain the individual settings object is defined here – and in this case is called “Settings”.

[Serializable]
public class EFTConfigurationSection : ConfigurationSection
{
    [ConfigurationProperty("Settings")]
    public EFTSettingsCollection Settings
    {
        get { return ((EFTSettingsCollection)(base["Settings"])); }
    }
}

Note:
The above was all written within the namespace “EFTLibrary.Settings” (you will see why this is important in the final step.

Step 2: Add the settings to the configuration file

<EFTConfiguration>
  <Settings>
    <add Name="setting1" Value1="Some Value" Value2="More Values" Value3="Some More Values" />
    <add Name="setting2" Value1="Some Value 2" Value2="More Values 2" Value3="Some More Values 2" />
  </Settings>
</EFTConfiguration>
This section goes within the <configuration> elements of the config file.

Step 3: Add a reference to the custom configuration class that tells .Net how to read the custom configuration section shown in step 2.

In the config file’s configSection section (<configuration><configSections>) we need to add a reference to the cllas that derives the ConfigurationSection class. (In this case EFTConfigurationSection). For this you need the name of the module in which the class resides (could be exe or dll) as well as the class name and the namespace within which it is defined.

<configuration>
  <configSections>
    <section name="EFTConfiguration" type="EFTLibrary.Settings.EFTConfigurationSection, EFTFileMoverLib" requirePermission="false"/>
  </configSections>

Here is how the section is formatted:

name: the name of the section in the config file which stores custom settings using the custom provider being defined in the type property.
type: Namespace.ClassName, ModuleName

And you should be in business!

Here is how you read the custom section in your code:

EFTConfigurationSection section = (EFTConfigurationSection)ConfigurationManager.GetSection("EFTConfiguration");
if (section != null)
{
    EFTSettingsElement settings = section.Settings["setting1"];
    if (settings != null)
    {
        value1 = settings.Value1;
        value2 = settings.Value2;
        value3 = settings.Value3;
    }
}

No comments: