I remember a while ago writing an application and wanting to feed in some configuration settings. I was just using AppSettings, but the problem was that all settings were strings, so I had to cast several variables, and write loads of error checking just to get the application up and running.

The application in question had several settings including a mixture of booleans, integers and strings. I had to parse them and then error check these values, to make the application more stable. It did create a heap of try/catch blocks that, as you will see are now unnecessary. Here is how the application used to work:

public void Initialize()
{
    int timeout = 0;
 
    if (int.TryParse(ConfigurationManager.AppSettings["Timeout"], out timeout))
    {
        if (timeout < 0)
            throw new ConfigurationErrorsException("Timeout must be >= 0");
        else
            this.Timeout = timeout;
    }
    else
    {
        throw new ConfigurationErrorsException("Timeout is not a valid integer");
    }
 
    ...
}

Now, although this is quite extensive error checking, the reasoning behind it is purely because we are dealing with strings. Can't the .NET framework do this for us!? YES!

In .NET 2.0, they have made it so easy to have your own Configuration Sections, and even making them type safe too! All with the help of the System.Configuration.ConfigurationSection (in System.Configuration.dll, not in System.dll).

All we have to do is create a class that inherits from System.Configuration.ConfigurationSection and then apply a couple of attributes! Then, we can give this class meaningful property names.

There are 4 steps to this:

  1. Create an entry in your Configuration file to hold the data
  2. Create a class to read the settings into
  3. Create an entry in your Configuration file to tell the CLR what class will it read the information info.
  4. Read the section from the ConfigurationManager (or WebConfigurationManager).

Create an entry in your Configuration file to hold the data

I would like to declare my XML like this:

<UseWebService available="true" timeToRespond="100" address="http://www.tempuri.org" />

Create a class to read the settings into

So lets say we want to store:

  • Enabled = Bool, must be defined
  • Timeout = Integer, Must be >= 0
  • Url = string, optional

We just create a class that inherits from System.Configuration.ConfigurationSection and declare these properties, and use get/set to set the values on the base class.

 

Therefore I will assign the "available" in the property "Enabled", "timeToRespond" to "Timeout" and "address" to "Url".

Firstly, I'll create my class. Notice that I'm not using the same text as the attribute in the XML - e.g. I use base["enabled"] instead of base["available"] ... This will be explained shortly.

public class UseWebServiceSection : ConfigurationSection
{
 
    public bool Enabled
    {
      get { return (bool)base["enabled"]; }
      set { base["enabled"] = value }
    }
 
    public int Timeout
    {
         get { return (int)base["timeout"]; }
         set { base["timeout"] = value }
    }
 
    public string Url
    {
         get { return (string)base["url"]; }
         set { base["url"] = value }
    }
}

This isn't enough. We're assigning the values, but the class can actually pull the values out and assign them for us with the use of the ConfigurationPropertyAttribute. This attribute allows us to assign which XML attribute will be bound to the property, but also set up default values too! So lets apply it to these values:

public class UseWebServiceSection : ConfigurationSection
{     
    [ConfigurationProperty("available", IsRequired= false, DefaultValue = true)]
    public bool Enabled    
    {      
        get { return (bool)base["enabled"]; }      
        set { base["enabled"] = value }    
    }
 
    [ConfigurationProperty("timeToRespond", IsRequired = true)]
    public int Timeout    
    {         
        get { return (int)base["timeout"]; }         
        set { base["timeout"] = value }    
    }     
    [ConfigurationProperty("address", IsRequired = true)]
    public string Url    
    {         
        get { return (string)base["url"]; }         
        set { base["url"] = value }    
    }
}

Lastly, there is a requirement that the Timeout field NEEDS to be >=0. So, we can use the IntegerValidatorAttribute to check this for us.

[IntegerValidator(MinValue=0)]
[ConfigurationProperty("timeToRespond",IsRequired=true)]
public int Timeout
{
    //As above
}

Create an entry in your Configuration file to tell the CLR what class will it read the information info

The most important part - the configuration file needs to know how to deal with this new section.

The configuration file has a section called "configSections". This element contains the definition for your new section, and what class to process it with. In our case it would look like:

<configuration>
    <configSections>
        <section name="UseWebService" type"UseWebServiceSection, MyNamespace.UseWebServiceSection"/>
    </configSection>
</configuration>

Read the section from the ConfigurationManager (or WebConfigurationManager).

Finally, the section now needs to be pulled out of the Configuration file.

UseWebServiceSection webService = (UseWebServiceSection)ConfigurationManager.GetSection("UseWebService");
Console.WriteLine("available = {0}",webService.Enabled);
Console.WriteLine("timeToRespond = {0}",webService.Timeout);
Console.WriteLine("address = {0}",webService.Url);

Summary

Having the ConfigurationSection class immensely simplifies creating type-safe configuration sections. It seems like a lot of work, but it isn't really. Just create a class, create your XML, add a section in the .config and then write a bit of code. Its easy when you know how!

In a later article, I will explain how you can write inner elements rather than just using attributes on one level.


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist
posted @ Monday, June 23, 2008 12:17 PM |

Comments

No comments posted yet.

Post Comment

Title *
Name *
Email
Url
Comment *  


Please add 7 and 2 and type the answer here: