DekGenius.com
[ Team LiB ] Previous Section Next Section

15.2 Adding Your Own Configuration Settings

There are two ways to add your own configuration settings to the configuration files. You can use the appSettings configuration section, which the .NET Framework knows how to interpret automatically, or you can add your own configuration section and write the code for the framework to access it.

15.2.1 Using the appSettings Element

The easiest way to add configuration settings to a configuration file is to use the appSettings element. Configuration settings added this way take the form of key-value pairs. These key-value pairs may be added anywhere in the hierarchy of configuration files. Once added, key-value pairs can be removed individually in a configuration file lower in the hierarchy, or the entire set can be cleared.

Example 15-1 shows an excerpt from a machine configuration file, with an appSettings element added.

Example 15-1. Excerpt from the machine configuration file
<?xml version="1.0" encoding="UTF-8" ?>

<configuration>
  ...
  <appSettings>
    <add key="some key" value="some value" /> 
  </appSettings>
  ...
</configuration>

This configuration file simply adds a configuration setting whose key is "some key" and whose value is "some value". The added key will be available to all .NET applications running on this machine.

Example 15-2 shows an application configuration file that uses the appSettings element to remove the key added in the machine configuration file and add another key.

Example 15-2. The application configuration file
<?xml version="1.0" encoding="UTF-8" ?>

<configuration>
    <appSettings>
        <remove key="some key" />
        <add key="some other key" value="some other value" />
    </appSettings>
</configuration>

The application that loads this application configuration file will no longer have the key-value pair with key "some key", but it will have a key-value pair with key "some other key". The application configuration file can also use the clear element to remove all the appSettings key-value pairs.

The key-value pairs are accessed using the System.Configuration.ConfigurationSettings class. Example 15-3 shows how you can access the key-value pairs defined in Examples Example 15-1 and Example 15-2.

Example 15-3. Program to read and display app settings
using System;
using System.Configuration;

public class AppSettingsTest {
  public static void Main(string [ ] args) {
    string someKey = "some key";
    string someOtherKey = "some other key";
    Console.WriteLine("\"{0}\"=\"{1}\"", someKey, 
                      ConfigurationSettings.AppSettings[someKey]);
    Console.WriteLine("\"{0}\"=\"{1}\"", someOtherKey, 
                      ConfigurationSettings.AppSettings[someOtherKey]);
  }
}

Running the AppSettingsTest executable, you can expect to see the following output:

"some key"=""
"some other key"="some other value"

That's fine, if you're only interested in string values. If you need to retrieve a different type of data, however, you can use the AppSettingsReader class. AppSettingsReader has only one method, GetValue( ), which takes as parameters the key name and the Type of data you wish to have returned. For example, take the following appSettings element:

<appSettings>
    <add key="some numeric key" value="5.00987" />
</appSettings>

To return that value as a decimal, you would use the following code:

AppSettingsReader reader = new AppSettingsReader( );
decimal value = (decimal)reader.GetValue("some numeric key",typeof(decimal));

Unlike ConfigurationSettings, AppSettingsReader has no static members. You must create an instance of AppSettingsReader before calling its GetValue( ) method.


15.2.2 Custom Elements

The appSettings element is actually a special case of the general mechanism for adding custom configuration elements, called the configuration section. A configuration section is simply an element in a configuration file. The ConfigSettings class knows how to read an arbitrary configuration section because you add a configSection element to the configuration file to tell it how. The appSettings configuration section is defined in the machine configuration file, as shown below:

<section name="appSettings" type="System.Configuration.NameValueFileSectionHandler, 
System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

I'll explain more about the section element in a moment. You can see, though, that the section element shows us that the appSettings section is handled by the System.Configuration.NameValueFileSectionHandler type. This type's Create( ) method reads the XML that is passed to it as an XmlNode, and creates the System.Collections.Specialized.NameValueCollection that is eventually returned by the ConfigSettings.AppSettings property.

The steps to create your own custom configuration sections are as follows:

  1. Select a configuration section handler to handle the configuration section. If none of the built-in configuration section handlers are appropriate, write your own class that implements IConfigurationSectionHandler.

  2. Add a section element to the configSections element of the machine or application configuration file, which links the section name to the type of the class you created in step 1.

  3. Add the configuration section defined in step 2 to the machine or application configuration file.

  4. Write the code to access the configuration settings using the ConfigSettings.GetConfig( ) method.

15.2.2.1 Choosing a configuration section handler

The IConfigurationSectionHandler interface requires only one method, Create( ), which returns an object containing the configuration section's settings in a useful form. This object will be returned by ConfigurationSettings.GetConfig( ). The .NET Framework includes five built-in configuration section handlers, which are listed below:


DictionarySectionHandler

Create( ) will return a Hashtable containing the key-value pairs of the configuration section element's child add elements. The configuration section may also contain remove and clear child elements. Because the Hashtable is more efficient for large numbers of key-value pairs, this handler should be reserved for use with a large number of configuration settings.


IgnoreSectionHandler

Used to indicate that the configuration system class should ignore this configuration section. The IgnoreSectionHandler.Create( ) method always returns null. This handler is typically used internally by the .NET Framework, although you can use it if you wish.


NameValueSectionHandler

Behaves similarly to DictionarySectionHandler, except that Create( ) returns a NameValueCollection rather than a Hashtable. This handler is better suited for configuration sections with fewer than ten settings.


NameValueFileSectionHandler

Behaves similarly to NameValueSectionHandler, except that it also allows the element to have a file attribute, which specifies an additional XML file to read for configuration settings. The external file's root element name must be the same as the current element. This is the handler used for appSettings configuration sections, as shown in Examples 15-1 and 15-2.


SingleTagSectionHandler

Create( ) will return a Hashtable containing key-value pairs of the element's attribute names and values. The element may not have any child nodes. You should use this handler if you configuration section will consist of a single element with one or more attributes.

For some purposes, none of the built-in configuration section handlers may be appropriate. In that case, you can write your own handler by implementing IConfigurationSectionHandler. This interface has only method to implement, Create( ). One of Create( )'s parameters is the XmlNode containing the configuration section element itself.

Perhaps the simplest custom section handler would just return the XML used to construct the configuration section. Example 15-4 shows an implementation of XmlSectionHandler that does that.

Example 15-4. A simple implementation of XmlSectionHandler
using System;
using System.Configuration;
using System.Xml;

public class XmlSectionHandler : IConfigurationSectionHandler {
  public object Create(object parent, object configContext, XmlNode section) {
    return section;
  }
}

If you use this XmlSectionHandler, you'll see that the Create( ) returns an instance of System.Configuration.XmlConfigElement, which is an undocumented subclass of XmlElement. You shouldn't care what concrete type it is, as long as you treat it as an XmlElement.

15.2.2.2 Defining the configuration section

Once you've selected or written a configuration section handler, you need to define the configuration section and tell the configuration system of how to deal with it. To do this, you add a section element to the configuration file's configSections element.

The section elements define how a configuration section is to be handled. The section element has two attributes, name and type. The name attribute matches the name of a configuration section element that appears in a configuration file, and the type attribute specifies the name of the IConfigurationSectionHandler instance that will handle the configuration section.

section elements may be organized into a hierarchy using section groups. The element that defines section groups is called, appropriately, sectionGroup, and its only attribute is name. You can nest as many sectionGroup elements as you want, and a sectionGroup may contain both section elements and other sectionGroup elements.

Example 15-5 shows the configSections element of an application configuration file using the XmlSectionHandler in a configuration section group.

Example 15-5. Application configuration using an XmlSectionHandler in a configuration section group
<configSections>
    <sectionGroup name="Group">
        <section name="Custom" type="XmlSectionHandler, AppSettingsTest" />
    </sectionGroup>
</configSections>

The type attribute contains the full class name of the configuration section handler in standard .NET style: class name, assembly name, version, culture, and public token key. You can omit the version, culture, and public key token if the assembly is not in the global assembly cache. In this case, XmlSectionHandler is contained in the same assembly as the AppSettingsTest executable.


15.2.2.3 Adding the configuration section

The next step is to add the configuration section itself to the configuration file. Because of the way I wrote the XmlSectionHandler, it will accept any XML content. The only restriction on how it's used in the configuration file is that it must appear in the section group as defined in Example 15-5. Example 15-6 shows the complete application configuration file containing the configSections element, the Custom element I defined in the configSections element, and the appSettings element from Example 15-2.

Example 15-6. Complete application configuration file
<?xml version="1.0" encoding="UTF-8" ?>

<configuration>
    <configSections>
        <sectionGroup name="Group">
            <section name="Custom" type="XmlSectionHandler, AppSettingsTest" />
        </sectionGroup>
    </configSections>
  
    <appSettings>
        <remove key="some key" />
        <add key="some other key" value="some other value" />
        <add key="some numeric key" value="5.00987" />
    </appSettings>

    <Group>
        <Custom>
            <someElement attribute="attribute1">some
content &amp; stuff
<![CDATA[Some <text> that the parser won't even try to parse
]]>
            </someElement>
        </Custom>
    </Group>

</configuration>

Remember that you don't need to add a section element for the appSettings configuration section in your application configuration file because it's already included in the machine configuration file. Also, be sure to note that, although I've put it in the application configuration file, all of this configuration could also go in the machine configuration file.


15.2.2.4 Reading the custom configuration programmatically

Now you're ready to actually use the configuration data in the application configuration file. You already know how to access configuration settings in the appSettings section using ConfigurationSetting.AppSettings property. You can also access them using the ConfigurationSettings.GetConfig( ) method. The only parameter to GetConfig( ) is a string containing the path to the configuration section you want to get.

In fact, the AppSettings property is simply a proxy that calls the GetConfig( ) method, passing the string "appSettings".


Example 15-7 shows a program that gets configuration settings from the appSettings and Custom configuration sections.

Example 15-7. Reading various configuration information programmatically
using System;
using System.Configuration;
using System.Xml;

public class AppSettingsTest {
  public static void Main(string [ ] args) {
    try {
      string someKey = "some key";
      Console.WriteLine("{0}={1}", someKey, 
                        ConfigurationSettings.AppSettings[someKey]);
      string someOtherKey = "some other key";
      Console.WriteLine("{0}={1}", someOtherKey, 
                        ConfigurationSettings.AppSettings[someOtherKey]);
  
      string someNumericKey = "some numeric key";
      AppSettingsReader reader = new AppSettingsReader( );
      Console.WriteLine("{0}={1}", someNumericKey, 
                        reader.GetValue(someNumericKey,typeof(decimal)));

      Console.WriteLine( );

      string custom = "Group/Custom";
      XmlNode customConfig = (XmlNode)ConfigurationSettings.GetConfig(custom);
      Console.WriteLine("{0}={1}", custom, customConfig);
      Console.WriteLine("{0}={1}", customConfig.Name, customConfig.InnerXml);
            
    } catch (Exception e) {
      Console.Error.WriteLine(e);
    }
  }
}

public class XmlSectionHandler : IConfigurationSectionHandler {
  public object Create(object parent, object configContext, XmlNode section) {
    return section;
  }
}

Running this program, you'll see the following output:

some key=
some other key=some other value
some numeric key=5.00987

Group/Custom=System.Configuration.ConfigXmlElement
Custom=<someElement attribute="attribute1">some
content &amp; stuff
<![CDATA[Some <text> that the parser won't even try to parse
]]></someElement>

When calling ConfigurationSettings.GetConfig( ), you are responsible for knowing what type of object is being returned by the configuration section handler. If you cast the returned object to an incompatible type, an InvalidCastException will be thrown.

It's particularly important to be aware of return types if you try to use NameValueSectionHandler and DictionarySectionHandler interchangeably. Although both handlers will happily read the same XML from the configuration file, NameValueSectionHandler returns a NameValueCollection, while DictionarySectionHandler returns a Hashtable.

    [ Team LiB ] Previous Section Next Section