Click here to Skip to main content
Click here to Skip to main content

EnhancedSettings - Enhancing application config

, 3 Sep 2003
Rate this:
Please Sign up or sign in to vote.
Enable multiple environment configurations in a single .config (no longer just appSettings).

Introduction

This is a follow on from my previous article EnhanceAppSettingsHandler, and to get a complete picture I would suggest reading both articles. The previous article discussed how to take the appSettings section of a configuration file and extend it's capabilities to allow us to store multiple sets of configuration in one .config file and decide which on to use at runtime based on information about our environment (hostname etc.).

This article takes it one step further and applies the principals learnt there to create a IConfigurationSectionHandler that can be used to extend any configuration section in the same way just by tweaking to format of the .config file.

Making it generic

The basic principles behind the final EnhancedAppSettings were

  • We replaced the section handler for the appSettings section with our own
  • We defined some new tags that could be contained within the appSettings section to allow us to define configuration sets and map these sets to environments
  • We processed the new format to build up an XmlNode of the old format that could then be processed by the original section handler
  • We maintain full backward compatibility for how the configuration is read so nothing is ever dependent on the new format

If we look at these again we can see that they can easily be applied to any section just as easily with only minor changes to the code

  • We no longer process add / remove / clear nodes explicitly, we just append any nodes that we haven't defined to the new node that we pass to an instance of the original handler
  • We change the tag names of the tags we have defined to include a es_ prefix to reduce the chances of overlap with the configuration we are extending
  • We add an es_baseAttributes tag to allow us to define the attributes to append to the root of our new node (e.g. the appSetting node)
  • We add an attribute to the section tag we are replacing the handler of called es_ParentHandlerType that tells us the type of the original section handler to use to process the new XmlNode we build up. This is specified in the normal way as is it for the normal section tag

This allows us to apply the same principles to any section of configuration that is accessed using ConfigurationSettings.GetConfig (or ConfgiurationSetting.AppSettings) whether part of the original framework or a custom section defined by a third party.

Limitations

This method can only extend section that are accessed using ConfigurationSettings.GetConfig, it cannot extend sections that are read directly from the file - i.e. any sections that use the IgnoreSectionHandler. This means the following sections cannot be extended using this process

  • runtime
  • mscorlib
  • startup
  • system.runtime.remoting

Using the new handler

Making use of the handler requires 3 basic steps:

  1. Make sure the assembly containing the new handler is available to our application
  2. Change the .config file to register our new handle for the section we are extending
  3. Modify the configuration in the section we are extending to use the new features

Registering the new handler

To register the new handler against the section we are extending we need to add some lines at the top of the .config file. The following example replaces the handlers for the appSettings and system.web\customErrors sections.

<configSections>
    <remove name="appSettings"/>
    <section name="appSettings" 
          type="Haley.EnhancedSettings.EnhancedSettingsHandler,
                                    EnhancedSettings"/>
    <sectiongroup name="system.web">
        <remove name="customErrors"/>
        <section name="customErrors" 
          type="Haley.EnhancedSettings.EnhancedSettingsHandler,
                                    EnhancedSettings"/>
    </sectiongroup>    
</configSections>

To make use of these the start tags for the two sections then become

<appSettings es_ParentHandlerType=
        "System.Configuration.NameValueFileSectionHandler, System, 
         Version=1.0.5000.0, Culture=neutral, 
         PublicKeyToken=b77a5c561934e089">
<customErrors es_ParentHandlerType=
        "System.Web.Configuration.CustomErrorsConfigHandler, System.Web, 
        Version=1.0.5000.0, Culture=neutral, 
        PublicKeyToken=b03f5f7f11d50a3a">

and any attributes that would normally be attached directly to these tags are moved to a es_baseAttributes tag. To find the current handler for a particular section first look in the configSections element in the current config file for the application (there may not be one), otherwise refer to the machine.config.

Extending the configuration

This is an example of a new appSettings section, for more details on how this is processed see the previous article EnhancedAppSettingsHandler

<appSettings es_ParentHandlerType=
        "System.Configuration.NameValueFileSectionHandler,
        System, Version=1.0.5000.0, 
        Culture=neutral, PublicKeyToken=b77a5c561934e089">
    <es_baseAttributes 
        thisAttrib="this would break the current specified 
        parent hander as it doesn't support thisAttrib" />
    <!-- this causes us to clear the attributes of the 
                         parent and stop it breaking -->
    <es_baseAttributes /> 
    
    <es_configMap hostname="machine1">
        <es_include set="Dev"/>
    </es_configMap>
    
    <es_configMap hostname="machine2">
        <es_include set="Test"/>
    </es_configMap>
    
    <es_configMap hostname="machine3">
        <es_include set="prod"/>
    </es_configMap>
    
    <!--common to all -->
    <add key="key5" value="value5"/>
    <add key="key6" value="value6"/>
    
    <!-- Dev enivironment -->
    <es_configSet name="Dev">
        <add key="environmentName" value="dev"/>
        <add key="key1" value="dev_value1"/>
        <add key="key2" value="dev_value2"/>
    </es_configSet>
    
    <!-- included in all non-Dev enivironments -->
    <es_configSet name="NonDev">
        <add key="key1" value="nondev_value1"/>
        <add key="key2" value="nondev_value2"/>
    </es_configSet>
    
    <!-- Test enivironment -->
    <es_configSet name="Test">
        <add key="environmentName" value="UAT"/>
        <add key="key3" value="uat_value" />
        <es_include set="NonDev" />
    </es_configSet>
    
    <!-- Production enivironment -->
    <es_configSet name="prod">
        <add key="environmentName" value="Production"/>
        <add key="key3" value="prod_value" />
        <es_include set="NonDev" />
        <remove key="key1" />
    </es_configSet>
</appSettings>

Further extensions

This implementation uses the hostname to decide which configMap nodes to process. It is not always the case that the hostname is the defining factor, it may be:

  • The name of the virtual directory
  • A regular expression match on the hostname
  • The domain the user running the process is logged into
  • Even the actual username running the process (although I wouldn't recommend storing user level config in this way) The only restriction is that if the value of the item you use changes during the execution lifetime of the application the configuration will not be reread.

To change this part of the process there are two steps:

  1. Define a new attribute of the configMap node for the data we wish to match (e.g. virtDir)
  2. Create a new class that inherits from Haley.EnhancedSettings and overrides CheckConfigMap

Here is an example that uses an attribute called hostnameRegEx to allow regular expressions to be specified to match the hostname, it allows either the hostname or hostnameRegEx to be specified and matched, this means we can use hostnameRegEx="dev*" to match any machines with hostnames starting dev

protected override bool CheckConfigMap(XmlNode configMap)
{
    string hostname = null;
    string hostnameRegEx = null;
    XmlAttribute attrib = configMap.Attributes["hostname"];
    if (attrib!=null)
        hostname = attrib.Value;
    attrib = null;
    attrib = configMap.Attributes["hostnameRegEx"];
    if (attrib!=null)
        hostnameRegEx = attrib.Value;
    if ((hostname==null) && (hostnameRegEx==null))
        throw new ConfigurationException("Missing hostname o 
                     configuration node: " + configMap.OuterXml);

    return CheckHostname(hostname) || CheckHostnameRegEx(hostnameRegEx);
}

private bool CheckHostnameRegEx(string hostnameRegEx)
{
    if (hostnameRegEx==null)
        return false;
    else
    {
        Regex re = new Regex(hostnameRegEx, RegexOptions.IgnoreCase);
        return re.IsMatch(System.Environment.MachineName);
    }
}

NB: I have now integrated this into the project

History

  • 30 August 2003 - Version 4.0
  • 08 September 2003 - Version 4.1 - Fixed thread safety issue

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

Share

About the Author

Paul Haley
Web Developer
United Kingdom United Kingdom
Paul has a background in VB / C++ COM development and has now converted to dotNET, coding in C# for the financial markets industry for the last 3 years.

Comments and Discussions

 
GeneralCode No longer works in .NET 2.0 Framework PinmemberJasmeet Sangari2-Mar-08 17:47 
GeneralRe: Code No longer works in .NET 2.0 Framework PinmemberPaul Haley2-Mar-08 22:03 
GeneralRe: Code No longer works in .NET 2.0 Framework Pinmemberbobjones6311726-Mar-08 6:20 
GeneralNameValueFileSectionHandler File attribute not supported PinmemberSaurabh Gulati1-Aug-06 20:20 
GeneralRe: NameValueFileSectionHandler File attribute not supported PinmemberSaurabh Gulati2-Aug-06 2:29 
GeneralRe: NameValueFileSectionHandler File attribute not supported PinmemberPaul Haley6-Aug-06 13:42 
GeneralI need costumize an error PinmemberAzaliam31-May-06 6:52 
QuestionHow to get it to work PinmemberLogan573525-Jan-06 8:10 
AnswerRe: How to get it to work PinmemberPaul Haley30-Jan-06 11:39 
GeneralRe: How to get it to work Pinmemberxyzray14-Sep-06 6:06 
GeneralRe: How to get it to work Pinmemberxyzray15-Sep-06 5:07 
GeneralIssue with appSettings and SmartClient PinsussGeir Aamodt4-Apr-05 3:59 
GeneralRe: Issue with appSettings and SmartClient Pinmemberbingobonzo4-Apr-05 4:01 
GeneralRe: Issue with appSettings and SmartClient Pinmemberbingobonzo4-Apr-05 4:20 
GeneralRe: Issue with appSettings and SmartClient PinmemberPaul Haley4-Apr-05 12:16 
GeneralRe: Issue with appSettings and SmartClient PinmemberPaul Haley4-Apr-05 12:13 
GeneralRe: Issue with appSettings and SmartClient PinmemberGeir Aamodt6-Apr-05 0:59 
GeneralRe: Issue with appSettings and SmartClient PinmemberPaul Haley6-Apr-05 13:19 
GeneralRe: Issue with appSettings and SmartClient PinmemberGeir Aamodt6-Apr-05 21:58 
GeneralRe: Issue with appSettings and SmartClient PinmemberPaul Haley7-Apr-05 13:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.1411023.1 | Last Updated 4 Sep 2003
Article Copyright 2003 by Paul Haley
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid