Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom Objects From the App.Config file

0.00/5 (No votes)
21 Apr 2004 1  
Defining custom objects in the App.Config file, and retrieving them via the IConfigurationSectionHandler Interface.

Introduction

This article describes how to implement custom objects in the App.Config file, enabling data to be stored in XML format, and to be easily deserialized into a custom object.

Background

There is an assumption that the reader is reasonably familiar with XML Serialization from the System.Xml.Serialization namespace. For an object to be usable in the App.Config file, it needs to be Serializable. Refer to Introducing XML Serialization in the MS Help Files.

Using the code

For this example, I have created two classes that will be configured in the App.Config file. The first is a simple class designed to illustrate how to configure a simple class for deserialization, the second is more complex, and involves a collection class as well. I've included the second class and associated classes in the example code, but only describes the first class. The extra complexity is not related to custom configuration handling, but more to XML Serialization, so I leave it up to the reader to read the code in conjunction with the XML Serialization documentation for a better understanding.

For the first example, I have created a class called ProgramInfo which contains three public properties:

  • Name
  • Language
  • Level

The first two are implemented as public strings, the third uses property assessors. This is a better way to go, I included the first two to illustrate how Serialization works. As long as the element has the same name, it will be found, and it doesn't matter how you implement the property.

The first piece of code to note in the App.Config file is the element telling the CLR that we have a custom configuration section. It is in an element called section, child of configSections, direct child of the configuration document element. It should look like this:

<configuration>
 <configSections>
   <section name="ProgramInfo" 
     type="ConfigSectionHandler.ConfigSectionHandler, ConfigSectionHandler" />
 </configSections>
</configuration>

The main element of interest is the section element. This states that there will be an element with the same name as contained in the name attribute, in this case, ProgramInfo. The type attribute states that the CLR should instantiate an instance of the ConfigSectionHandler.ConfigSectionHandler class, located in the ConfigSectionHandler assembly. The syntax is the same as that used by the Type class.

Before we look at the ConfigSectionHandler class, let's take a look at the ProgramInfo section further down in the configuration file.

<ProgramInfo type="ConfigSectionObjects.ProgramInfo, ConfigSectionObjects">
  <Name>ConfigSectionDemo</Name>
  <Language>C#</Language>
  <Level>Intermediate</Level>
</ProgramInfo>

This is straightforward XML, but note the type attribute. This once again uses the same syntax to describe what object should be instantiated to contain the information contained in this XmlNode. This information is not part of the Configuration system. This complete node, all information contained in it is for use by the ConfigSectionHandler that we will be using.

Let's look at an abbreviated version of the ProjectInfo class. The code looks as follows:

namespace ConfigSectionObjects
{
  public class ProgramInfo
  public ProgramInfo()    {
  }

  public string Name;
  public string Language;

  private string _Level;
  public string Level 
  {
    get {return _Level;}
    set {_Level=value;}
  }
}

A fairly simple class. I implemented the three properties in two different ways, the first two use public variables, which is not really a good way to handle property access. The last property uses accessor methods.

Note: the combination of the Namespace and the Class name gives us ConfigSectionObjects.ProgramInfo which is the value we specified in the type attribute of the ProgramInfo element in our custom configuration section.

The class name is the same as the element name in the custom section.

The two public variables and the one public property map directly to the children of the ProgramInfo element. These values all map directly. This does not have to be the case, there are methods to map an element name to a different property, or to have attributes map to properties. These will be discussed briefly, later.

At this point, we have specified in our App.Config file all the information we need, and we have also defined the class that will be deserialized from the App.Config file. Now, we need to use some code to handle the deserialization. To do this, we need to use the GetConfig static method of the ConfigurationSettings class, and tell it which section we want. This method's return type is Object, so we need to cast it back to the type we require. The code is as follows:

ConfigSectionObjects.ProgramInfo pi=
  (ConfigSectionObjects.ProgramInfo)ConfigurationSettings.GetConfig("ProgramInfo");

We declare a variable of the type that we will be deserializing, and cast the returned object from the method. Now, the variable pi is a valid instance of ConfigSectionObjects.ProgramInfo, so we can access its properties like so:

Console.WriteLine ("Program: {0}", pi.Name);
Console.WriteLine ("Language: {0}", pi.Language);
Console.WriteLine ("Level: {0}", pi.Level);

Now, it is worth looking at the actual ConfigSectionHandler code. It is remarkably simple. It is one method called Create which is in a class that implements the IConfigurationSectionHandler interface. This method is called when the line of code we specified above with the GetConfig call is encountered. The objects passed in are passed in by the CLR. The MSDN documentation states the following:

  • parent: the configuration settings in a corresponding parent configuration section.
  • configContext: an HttpConfigurationContext when Create is called from the ASP.NET configuration system. Otherwise, this parameter is reserved and is a null reference (Nothing in Visual Basic).
  • section: The XmlNode that contains the configuration information from the configuration file. Provides direct access to the XML contents of the configuration section.

It is only section that we are interested in. This is the XmlNode that we defined in the App.Config file. So we need to do a few steps:

  • Create an XPathNavigator object.
  • Use the XPathNavigator to get the type attribute that we have defined in the section.
  • Create a Type object based on the attribute value.
  • Create an XmlSerializer based on the type.
  • Create an XmlNodeReader using our custom section.
  • Call Deserialize and return the deserialized object.

The code follows:

XPathNavigator xNav=section.CreateNavigator();
string typeOfObject=(string) xNav.Evaluate("string(@type)");
Type t=Type.GetType(typeOfObject);
XmlSerializer ser=new XmlSerializer(t);
XmlNodeReader xNodeReader=new XmlNodeReader(section);
return ser.Deserialize(xNodeReader);

Complex Serialization

It is possible to do more complex serialization. For example, in the ProgramInfo example, if we wanted to rename the name element in the ProgramInfo class to Description, but leave the App.Config file alone, it is as simple as adding an attribute above the variable declaration. Like this:

[XmlElement("Name")]
public string Description;

This is telling the deserializer, that when the Name element is found in the configuration section, then its value should be placed in the Description property of the ProgramInfo class.

Alternatively, if rather than having Level as an element, you wanted to have it as an attribute of ProgramInfo, then, the App.Config file could be changed to remove the Level element, and add it as an attribute, then change the ProgramInfo class to read:

[XmlAttribute("Level2")]
public string Level2;

Once again, refer to the example code, the ServerConfig section for a more complex example.

Points of Interest

I found I made a lot of errors when defining the type attributes, I would get the assembly name correct, but forget to fully qualify the class name with the namespace. When this happens, the code will throw a System.Configuration.ConfigurationException.

I also found the documentation very vague when it came to configuration files.

History

  • 20-Apr-2004 - version 1.0.1 - corrected typo about changing level element to an attribute.
  • 13-Apr-2004 - version 1.0.0.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here