5,135,153 members and growing! (13,119 online)
Email Password   helpLost your password?
Languages » C# » General     Intermediate License: The Code Project Open License (CPOL)

Strongly typed custom configuration sections using XML serialization

By Benzi K. Ahamed

This article demonstrates a simple approach to get strongly typed configuration objects to use in your code using XML serialization
C# (C# 1.0, C# 2.0, C# 3.0, C#), .NET (.NET, .NET 1.1, .NET 2.0), Win32, ASP.NET, Dev

Posted: 29 Dec 2007
Updated: 7 Jan 2008
Views: 5,039
Announcements



Search    
Advanced Search
Sitemap
2 votes for this Article.
Popularity: 1.20 Rating: 4.00 out of 5
0 votes, 0.0%
1
0 votes, 0.0%
2
1 vote, 50.0%
3
0 votes, 0.0%
4
1 vote, 50.0%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

Introduction

This article describes a simple mechanism to use to XML serialization within configuration files to support strongly typed configuration items in your projects.

Background

This article uses the following concepts (and readers are expected to have a basic understanding of the same):

  1. Generic classes
  2. XML serialization
  3. Handling configuration files

Using the code

Note: The code provided is a VS2008 solution file containing two projects: The code is provided as two separate solution file downloads, one uses the IConfigurationSectionHandler interface (which is deprecated in .NET v2.0 and above), the other uses the ConfigurationSection class to implement the same. Both projects are made up of two class libraries:

  1. Citrus.Configuration - Class library
  2. Citrus.Configuration.Demo - Demo application

Strongly typed configuration entities are very much advantageous in terms of code clarity and readability, ease of programming etc. Consider a hypothetical situation where we need to save the name and IP of an email server within our configuration file. We need to be able to retrieve these details from the configuration file as strongly typed entities. Meaning, we could do with a EmailServerSettings object that has two properties Name and IP that can be accessed off of it.

Of course this is just an example, and the approach can be easily reused and extended for configurable entities of your choice.

First thing is to define the EmailServerSettings class that represents our strongly typed entity.

    /// 
    /// This is a sample class that is used
    /// to describe XML serialization based configuration
    /// section usage
    /// 
    [XmlRoot("EmailServerSettings")]
    public class EmailServerSettings
    {
        [XmlElement("IP")]
        public string IP { get; set; } // note the use of implicit property accessors in C# 3.0

        [XmlElement("Name")]
        public string Name { get; set; }

        #region Overrides
        public override string ToString()
        {
            return String.Format("{0} email server ({1})", Name, IP);
        }
        #endregion
    }

This is really straightforward. To allow for XML serialization, we mark up the properties and class with the necessary XML attributes.

.NET 1.1 Approach

Next, we define a generic XML serialization based configuration section handler. What this class does is it implements the IConfigurationSectionHandler interface. This interface is deprecated in .NET version 2.0 and above. For implementing the same in .NET 2.0, refer the section .NET 2.0 Approach

    /// 
    /// Handles the access to a generic configuration section using
    /// XML serialization
    /// 
    /// The type of the configuration section
    public class XmlSectionHandler<T> : IConfigurationSectionHandler
    {
        #region Properties and fields

        private XmlSerializer serializer = new XmlSerializer(typeof(T));
        public XmlSerializer Serializer { get; set; }

        #endregion

        #region IConfigurationSectionHandler Members

        public virtual object Create(object parent, object configContext, XmlNode section)
        {
            return serializer.Deserialize(new StringReader(section.OuterXml));
        }

        #endregion
    }

XmlSectionHandler is a generic class; it uses the type associated with it to create a serializer that does the core XML deserialization.

At this stage we have the fundamental blocks required; a simple helper class that gives us the strongly typed configuration entities is also written.

    /// 
    /// Configuration helper class that exposes generic
    /// section retrieval functionality
    /// 
    public static class ConfigurationHelper
    {
        /// 
        /// Retrieves a typed configuration section for 
        /// the current application's default configuration
        /// 
        /// The type to bind to the configuration section
        /// The name of the configuration section
        /// 
        public static T GetSection<T>(string sectionName)
        {
            return (T)ConfigurationManager.GetSection(sectionName);
        }

        /// 
        /// Retrieves a typed configuration section for 
        /// the current application's default configuration
        /// 
        /// The type to bind to the configuration section
        /// 
        public static T GetSection<T>()
        {
            return (T)ConfigurationManager.GetSection(typeof(T).Name);
        }

    }

All the helper class does is it provides some userful routines that casts the configuration objects to the specified type. That's it. These three classes form the core classes in the Citrus.Configuration class library.

To use them, first we need to update our configuration file. The following is a simple configuration file that uses EmailServerSettings and XmlSectionHandler in a custom cofig section

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <!-- The following is used to describe our custom section -->
    <section name="EmailServerSettings"
type="Citrus.Configuration.XmlSectionHandler`1
      [[Citrus.Configuration.EmailServerSettings,
      Citrus.Configuration,
      Version=1.0.0.0,
      Culture=neutral, 
      PublicKeyToken=null]], 
      Citrus.Configuration, 
      Version=1.0.0.0, Culture=neutral, 
      PublicKeyToken=null" />
  </configSections>
  <!-- Now that our section has been declared, use it -->
  <EmailServerSettings configSource="EmailServerSettings.config" />
</configuration>

The external EmailServerSettings.config file is essentially the XML serialized version of the class.

<!-- A simple XML file containing settings for a email server -->
<EmailServerSettings>
  <Name>Coldmail</Name>
  <IP>127.0.0.1</IP>
</EmailServerSettings>

Now we are ready to actually use all this in code:

   // The following will give us the email server settings
   EmailServerSettings emailServerSettings1 = ConfigurationHelper.GetSection("EmailServerSettings");
   EmailServerSettings emailServerSettings2 = ConfigurationHelper.GetSection();
   
   // Print the information out
   Console.WriteLine(emailServerSettings1);
   Console.WriteLine(emailServerSettings2);

In fact you can easily use emailServerSettings1.Name and emailServerSettings1.IP, giving us strongly typed configuration entities.

To extend this for your requirements, you would need to define your own configuration specific classes (like the EmailServerSettings class) and add an appropriate configuration section in your config file.

.NET 2.0 Approach

Since the IConfigurationSectionHandler is deprecated in this version, we have to redefine the XmlSectionHandler class as one that derives from ConfigurationSection instead.

public class XmlSection<T> : ConfigurationSection where T: class

(Note the class name has changed in the .NET 2.0 version). Subtle modifications have to be made to allow the class to handle XML serialization in this case. These changes include overriding the Init() and DeserializeSection() methods of ConfigurationSection. Also, XmlSection implements a basic support to save modified configuration details back to the configuration file. To use the XmlSection class, refer the code snippet below

// The object that represents our email server's settings
EmailServerSettings anEmailServer;
// Get the section that is specified an external configuration file
anEmailServer = XmlSection<EmailServerSettings>.GetSection("EmailSettings");
Console.WriteLine(anEmailServer);

// Update the section, refresh and load it again
Config.Configuration c = Config.ConfigurationManager.OpenExeConfiguration(Config.ConfigurationUserLevel.None);
XmlSection<EmailServerSettings>.GetSection("EmailSettings", c).Name = "Hello";
c.Save();
Config.ConfigurationManager.RefreshSection("EmailSettings");
anEmailServer = XmlSection<EmailServerSettings>.GetSection("EmailSettings");
Console.WriteLine(anEmailServer);

// Get settings that are specified inline
anEmailServer = XmlSection<EmailServerSettings>.GetSection("ProxyEmailServer");
Console.WriteLine(anEmailServer);

The corresponding config file is as:

<configuration>
    <configSections>
        <section name="EmailSettings"
type="Citrus.Configuration.XmlSection`1
[[Citrus.Configuration.EmailServerSettings, Citrus.Configuration]],
Citrus.Configuration"/>
        <section name="ProxyEmailServer"
type="Citrus.Configuration.XmlSection`1
[[Citrus.Configuration.EmailServerSettings, Citrus.Configuration]],
Citrus.Configuration"/>
    </configSections>
    <EmailSettings configSource="EmailSettings.config" />
    <ProxyEmailServer>
        <Name>Proxy</Name>
        <IP>127.0.0.1</IP>
    </ProxyEmailServer>    
</configuration>

Let me know if all this is helpful in some way to you. Enjoy.

History

[ .] Initial version
[+] New release. .NET 2.0 specific implementation using the ConfigurationSection class
[+] 7-Jan-2008. (v1.1). In the previous version, XmlSerializers were created on the fly using the XmlSerializer(Type, XmlRootAttribute) constructor. By nature of design, using this constructor is not very efficient. Therefore, a simple SerializerCache is implemented that caches XmlSerializer objects so that they can be reused, making usage faster and efficient. Another minor change made was to move the EmailServerSettings class outside of the Citrus.Configuration library.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Benzi K. Ahamed


Works as a software engineer by day, evil coder trying to take over the world at night.

Passions include programming methodologies, compiler theory, cartooning and calligraphy.
Occupation: Web Developer
Location: United Kingdom United Kingdom

Other popular C# articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Search Search Messages 
 Layout  Per page   
 Msgs 1 to 3 of 3 (Total in Forum: 3) (Refresh)FirstPrevNext
Subject  Author Date 
GeneralInteresting and a few questionsmemberBillWoodruff23:10 20 Jan '08  
GeneralNot needed any more in .NET 2.0mvpRama Krishna Vavilala11:41 29 Dec '07  
GeneralRe: Not needed any more in .NET 2.0memberBenzi K. Ahamed5:21 4 Jan '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 7 Jan 2008
Editor:
Copyright 2007 by Benzi K. Ahamed
Everything else Copyright © CodeProject, 1999-2008
Web10 | Advertise on the Code Project