Introduction
This article details how to create custom configuration sections in Microsoft .NET Framework 2.0 based applications. .NET 2.0 introduces a new namespace System.Configuration to handle application configuration files. This namespace exposes several classes to work with configuration files. The ConfigurationManager is a static class in the System.Configuration namespace which is most widely used to access any application settings, connection strings, section groups and custom sections. This also exposes methods to open and access configuration files other than the default application configuration file (ie .exe.config, .dll.config, web.config).
Configuration File
A sample configuration file snippet is shown below. This article describes how to build custom configuration section for the configuration file shown below.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="application">
<section name="settings" type="Configurator.BaseConfigurationSection, Configurator"/>
</sectionGroup>
</configSections>
<application>
<settings description="Key value pair settings">
<keys>
<add key="key1" value="value1"/>
<add key="key2" value="value2"/>
<add key="key3" value="value3"/>
<add key="key4" value="value4"/>
<add key="key5" value="value5"/>
</keys>
</settings>
</application>
</configuration>
Code snippet - 1
Class design
The class diagram shown below gives a high level picture of the classes involved in creating the custom configuration section. The inheritance hierarchy is also shown to highlight all the participant classes from the System.Configuration namespace and their usage whilst creating the class model.
![clip_image002[4]](http://lh4.google.com/ramamurthy.k/R-WGwisIbeI/AAAAAAAABJE/C9yQk-b82pM/clip_image002%5B4%5D%5B12%5D.gif)
Figure - 1
A typical configuration file will have the following structure.
<sectionGroup>
<section atribute=”id”>
<element1/>
<element2/>
<element3/>
</section>
</sectionGroup>
Code snippet - 2
A sectionGroup element groups all the custom section entries. It can have multiple sections for which the section handler has to be defined in the code in order to access individual section entries. Based on the application’s requirement one has to judge the groupings and the hierarchies that should go within the configuration file. A typical section group would include application level settings. Eg: application constants, User interface mappings, file paths etc.
The primary concentration here is to create a custom configuration section and how to access it from the application. The above configuration file snippet provides the structure for a configuration section. It can have attributes and nested elements as part of the declaration.
Class Definition
System.Configuration namespace has an abstract class ConfigurationElement which represents a configuration element within a configuration file. Both ConfigurationSection and ConfigurationElementCollection abstract classes inherit from ConfigurationElement class. The following configuration entry can be represented as an instance of a class derived from the ConfigurationElement.
<add key="key1" value="value1"/>
Code snippet - 3
We can define the following class structure to represent the above configuration snippet.
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
namespace Configurator
{
public class BaseConfigurationElement : ConfigurationElement
{
public BaseConfigurationElement() : base() { }
[ConfigurationProperty("key", IsRequired = true)]
public string Key
{
get { return (string) base["key"]; }
}
[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get { return (string)base["value"]; }
}
}
}
Code snippet - 4
The BaseConfigurationElement class inherits the ConfigurationElement abstract class. This class exposes two properties which represents key and value attributes which are declared as part of the <add/> element. The mapping between the property name and the actual configuration file entry is established by declarative attribute attached to the property. This tells the run time that the property Key must be associated to the xml attribute key from the configuration file.
We have now defined the ConfigurationElement which holds key value pairs. What if we need to hold to multiple key value pairs? This can be achieved with the help of collection classes. ConfigurationElementCollection class is an abstract class within the System.Configuration namespace which represents the collection of configuration entries. Since this is an abstract class, we need to inherit from this class and provide definition for the protected members and associate the collection to the configuration element.
Configuration collection snippet:
<keys>
<add key="key1" value="value1"/>
<add key="key2" value="value2"/>
<add key="key3" value="value3"/>
<add key="key4" value="value4"/>
<add key="key5" value="value5"/>
</keys>
Code snippet - 5
To represent the above structure, we need to create collection class which holds <add> elements. The class definition is shown below:
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
namespace Configurator
{
public class BaseConfigurationElementCollection<T> : ConfigurationElementCollection where T : ConfigurationElement, new()
{
public BaseConfigurationElementCollection() : base() {}
public T this[int index]
{
get { return (T) base.BaseGet(index); }
}
protected override ConfigurationElement CreateNewElement()
{
return new T();
}
protected override object GetElementKey(ConfigurationElement element)
{
return (T)element;
}
}
}
Code snippet - 6
The BaseConfigurationElementCollection<T> is defined as a class which accepts T as its generic parameter and inherits from the ConfigurationElementCollection abstract class. Note that the base class is not a generic type and we are introducing the generic type T which represents the ConfigurationElement. This also defines constraint on T that it should be of any type derived from the ConfigurationElement and must define a public parameterless constructor. This class provides an indexer to access ConfigurationElement objects from the collection. Methods CreateNewElement() and GetElementKey() must be overridden and the definition has to be provided; otherwise this class also becomes an abstract class which can’t be instantiated directly. The reason for introducing the genric type T is to ensure that we can use the same collection class for representing collection of elements which are derived from the ConfigurationElement.
Now, we are done with our collection class and let’s use this collection within in a section class. ConfigurationSection is an abstract class defined in the System.Configuration namespace which represents a configuration section. We are defining the BaseConfigurationSection class which inherits the ConfigurationSection abstract class. The BaseConfigurationSection exposes properties to access data defined within this section.
The configuration file and the class definition snippets are shown below:
<settings description="Key value pair settings">
<keys>
<add key="key1" value="value1"/>
<add key="key2" value="value2"/>
<add key="key3" value="value3"/>
<add key="key4" value="value4"/>
<add key="key5" value="value5"/>
</keys>
</settings>
Code snippet - 7
The class definition for BaseConfigurationSection is shown below:
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
namespace Configurator
{
public class BaseConfigurationSection : ConfigurationSection
{
public BaseConfigurationSection() : base() {}
[ConfigurationProperty("description")]
public string Description
{
get { return (string) this["description"]; }
}
[ConfigurationProperty("keys")]
public BaseConfigurationElementCollection<BaseConfigurationElement> Keys
{
get
{
return (BaseConfigurationElementCollection<BaseConfigurationElement>) this["keys"];
}
}
}
}
Code snippet - 8
The BaseConfigurationSection exposes properties to read the collection of key value pairs and the attribute which is declared as part of the section element in the configuration file. Note the association of these properties to the actual configuration entries is established through the declarative ConfigurationProperty attribute.
We are now done with our infrastructure required to access the custom configuration entry. We’ll now define the main program to read these values and display on the console.
The main program:
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
namespace Configurator
{
class Program
{
static void Main(string[] args)
{
BaseConfigurationSection settings = (BaseConfigurationSection)ConfigurationManager.GetSection("application/settings");
Console.WriteLine(settings.Description);
foreach(BaseConfigurationElement element in settings.Keys)
{
Console.WriteLine("Key: {0} Value: {1}", element.Key, element.Value);
}
Console.ReadKey();
}
}
}
Code snippet - 9
The console output:
Figure - 2
Conclusion
The Microsoft .NET Framework 2.0 has rich infrastructure to work with application configuration files. The custom configuration section handlers can be defined to read and write the data from the configuration sections. I have tried to make these classes as generic classes and so if there is any requirement to define multiple section groups and sections, these classes can be used as base classes to further provide the appropriate functionality.

1 comment:
Good work RAM. but difficult to digest for me.. Pinkesh
Post a Comment