Custom Configuration Section in C#


A very familiar way of implementing the frequently changing value in an application is by storing them in a configuration file’s <appSettings> section. When the project is big, storing too many values in application settings is not a suggested approach, as this can lead to confusions and many other integration problems. Many at times we have seen when the code is moved from a development environment to a production environment there is a mismatch in the the configuration or we loose out on some important values. These issues can be eliminated by simply using a custom configuration section. A project constitutes of many modules and each module will have configuration values specific to them. So it will be a good idea to group them to a single section, and every  module’s configuration values will reside in their respective section. This way by just looking at the configuration one can figure out which value relates to what module. It is also suggested to have a common section so that these values can be accessed by all the modules and will reduce duplication of values.

Going forward you will learn about creating custom configuration section, the corresponding value holder class, and properties to access them through out the application. I will make the demo simple to a console application, so that it is easy to understand.

Start a new Console application and add an application configuration file to it. Also reference System.Configuration.dll.

What’s in the Configuration?

  1. <?xml version=1.0 encoding=utf-8 ?>
  2. <configuration>
  3.     <configSections>
  4.         <section name=MyConfiguration type=ConfigurationC.MyConfiguration, ConfigurationC/>
  5.     </configSections>
  6.     <!– Configuration settings for MyConfiguration Starts –>
  7.     <MyConfiguration
  8.             To=support@nnish.com
  9.             From=sales@nnish.com>
  10.         <Messages>
  11.             <Message Name=Email Value=email/>
  12.             <Message Name=Sms Value=sms/>
  13.             <Message Name=Tweet Value=tweet />
  14.         </Messages>
  15.     </MyConfiguration>
  16.     <!– Configuration settings for MyConfiguration Ends –>
  17. </configuration>

Lets break this into bits and pieces. Notice the <configSections> element –this is where you register your custom configuration. To add a custom configuration – add an element <section name=”” type=””/>.

  • name – represents the name of the custom configuration
  • type – used as <namespace.Type>, <AssemblyName> (Type is the corresponding class which inherits ConfigurationSection)

And the <MyConfiguration>element is nothing but the custom configuration section which contains custom values stored.  I will explain multiple ways of storing in the config files and accessing them from the application. In the config, I store some values as the attribute of the main element, the main element has the sub element <Messages> and that in turn has the collection of <Messages> having attributes “Name” and “Value”. This can be easily related to <add> element in the <appSettings>.

What’s in the Code?

Now let us go ahead and build the classes for accessing these values in the application.

Remember these:

  • There should be a class in the application representing each element (including the sub elements) in the configuration file.
  • Class holding the custom Configuration Section should inherit from CustomSection
    • public class MyConfiguration : ConfigurationSection
  • The attributes/elements can be represented as a property which is of type ConfigurationProperty in the parent class.
  • Class representing an element collection should inherit from ConfigurationElementCollection
    • public class MessageCollection : ConfigurationElementCollection
  • Class representing an element should inherit from ConfigurationElement
    • public class MessageElement : ConfigurationElement

Since <Message> is the last child element, write the corresponding class for it first, and then <Messages> which is its parent and it holds the collection of <Messages> and finally the class for <MyConfiguration> which has attributes represented as properties in itself.

Element: <Message> – Class: MessageElement

  1. using System.Configuration;
  2. namespace ConfigurationC
  3. {
  4.     /// <summary>
  5.     /// Class holds the <Message> element
  6.     /// </summary>
  7.     public class MessageElement : ConfigurationElement
  8.     {
  9.         // Holds the Name attribute of the Message
  10.         private static readonly ConfigurationProperty messageName =
  11.             new ConfigurationProperty(“Name”, typeof(string), string.Empty, ConfigurationPropertyOptions.IsRequired);
  12.         // Holds the Value attribute value of Message.
  13.         private static readonly ConfigurationProperty messageValue =
  14.             new ConfigurationProperty(“Value”, typeof(string), string.Empty, ConfigurationPropertyOptions.IsRequired);
  15.         public MessageElement()
  16.         {
  17.             base.Properties.Add(messageName);
  18.             base.Properties.Add(messageValue);
  19.         }
  20.         /// <summary>
  21.         /// Name
  22.         /// </summary>
  23.         [ConfigurationProperty(“Name”, IsRequired = true)]
  24.         public string Name
  25.         {
  26.             get { return (string)this[messageName]; }
  27.         }
  28.         /// <summary>
  29.         /// Value
  30.         /// </summary>
  31.         [ConfigurationProperty(“Value”, IsRequired = true)]
  32.         public string Value
  33.         {
  34.             get { return (string)this[messageValue]; }
  35.         }
  36.     }
  37. }

Explanation:

  • <Message> element has attributes “name” and “value” which are represented as ConfigurationProperty and its initialization takes the following parameters
    • name: The name of the configuration entity.  
    • type: The type of the configuration entity.
    • defaultValue: The default value of the configuration entity.
    • options:  One of the System.Configuration.ConfigurationPropertyOptions enumeration values
  • In the constructor add these properties to the base class Property Collection
  • Expose them as public property

Element: <Messages> – Class: MessageCollection

  1. using System.Configuration;
  2. namespace ConfigurationC
  3. {
  4.     [ConfigurationCollection(typeof(MessageElement), AddItemName = “Message”,
  5.          CollectionType = ConfigurationElementCollectionType.BasicMap)]
  6.     public class MessageCollection : ConfigurationElementCollection
  7.     {
  8.         protected override ConfigurationElement CreateNewElement()
  9.         {
  10.             return new MessageElement();
  11.         }
  12.         protected override object GetElementKey(ConfigurationElement element)
  13.         {
  14.             return ((MessageElement)element).Name;
  15.         }
  16.      
  17.         new public MessageElement this[string name]
  18.         {
  19.             get { return (MessageElement)BaseGet(name); }
  20.         }
  21.     }
  22. }

Explanation:

  • MessageCollection class is inherited from the abstract class ConfigurationElementCollection and two of its abstract methods CreateElement() and GetElementKey() needs to be overridden
  • CreateElement() creates the new instance of the MessageElement
  • GetElementKey() Gets the element key for a Message element (we define Name attribute as the key, so that the value can be retrieved using the name)
  • new public MessageElement this[string name] – add this to retrieve the value with the key name. For e.g: ConfigurationManager.AppSettings[“test”];

 

Element: <MyConfiguration> – Class: MyConfiguration (Putting them all together[ConfigurationSection ])

  1. using System.Configuration;
  2. namespace ConfigurationC
  3. {
  4.     public class MyConfiguration : ConfigurationSection
  5.     {
  6.         private static readonly ConfigurationProperty toAttribute =
  7.              new ConfigurationProperty(“To”, typeof(string), string.Empty, ConfigurationPropertyOptions.IsRequired);
  8.         
  9.         private static readonly ConfigurationProperty fromAttribute =
  10.              new ConfigurationProperty(“From”, typeof(string), string.Empty, ConfigurationPropertyOptions.IsRequired);
  11.         private static readonly ConfigurationProperty messagesElement =
  12.              new ConfigurationProperty(“Messages”, typeof(MessageCollection), null, ConfigurationPropertyOptions.IsRequired);
  13.         public MyConfiguration()
  14.         {
  15.             base.Properties.Add(toAttribute);
  16.             base.Properties.Add(fromAttribute);
  17.             base.Properties.Add(messagesElement);
  18.         }
  19.         /// <summary>
  20.         /// To
  21.         /// </summary>
  22.         [ConfigurationProperty(“To”, IsRequired = true)]
  23.         public string To
  24.         {
  25.             get { return (string)this[toAttribute]; }
  26.         }
  27.         
  28.         /// <summary>
  29.         /// From
  30.         /// </summary>
  31.         [ConfigurationProperty(“From”, IsRequired = true)]
  32.         public string From
  33.         {
  34.             get { return (string)this[fromAttribute]; }
  35.         }
  36.         /// <summary>
  37.         /// Messages Collection
  38.         /// </summary>
  39.         [ConfigurationProperty(“Messages”, IsRequired = true)]
  40.         public MessageCollection Messages
  41.         {
  42.             get { return (MessageCollection)this[messagesElement]; }
  43.         }
  44.     }
  45. }

Explanation:

  • Add all the Attributes and Elements as the ConfigurationProperty of corresponding type.

And the Program.cs

  1. using System;
  2. using System.Configuration;
  3. namespace ConfigurationC
  4. {
  5.    public class Program
  6.     {
  7.         public static MyConfiguration MyConfig;
  8.         static void Main(string[] args)
  9.         {
  10.             MyConfig = GetConfiguration();
  11.             Console.WriteLine(“This program comes with no warranty!”);
  12.             Console.WriteLine(“Values found in the configuration”);
  13.             Console.WriteLine(“To: “ + MyConfig.To);
  14.             Console.WriteLine(“From: “ + MyConfig.From);
  15.             Console.WriteLine(“Message Email: “ + MyConfig.Messages[“Email”].Value);
  16.             Console.WriteLine(“Message Sme: “ + MyConfig.Messages[“Sms”].Value);
  17.             Console.WriteLine(“Message Tweet: “ + MyConfig.Messages[“Tweet”].Value);
  18.             Console.ReadLine();
  19.         }
  20.         private static MyConfiguration GetConfiguration()
  21.         {
  22.             return (MyConfiguration)ConfigurationManager.GetSection(“MyConfiguration”);
  23.         }
  24.     }
  25. }

Above code snippet is self explanatory.

I hope I was able to walk you through a step by step process of a developing hello world Custom Configuration section.

Download the code!

Cheers!

Advertisements

11 thoughts on “Custom Configuration Section in C#

  1. What if I want to do this?

    The issue I am having is that my Job ConfigurationElement class has to have a property that returns the JOBDETAIL element. However, JOBDETAIL element needs to be a KeyValueConfigurationCollection since every JOBDETAIL can have different amount of key/values. I can get this to work if there is only one, JobDetail elements, but a collection of jobDetails won’t work. Any ideas?

    1. @Moditha – Collection of JobDetails will work, If i understand it correctly that your problem is very similar to the way i use Messages Collection, If you look at each Message Element has a Name and a Value which is nothing but a Key and Value. This will work very similar to the AppSettings KeyValueConfigurationCollection. Try this out and let me know if it worked.

  2. I’ve followed your example (and several others!) but I cannot get the GetSection method to return anything other than NULL. Does this method work in VS2010 do you know?

  3. Glad that yours got fixed. 🙂 You can download my sample and tweak it for your need. It works with VS 2010.

  4. here what I do:

    ——————-
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Configuration;
    using System.Collections.Specialized;

    namespace tryConfigApp
    {
    class Program
    {
    static void Main(string[] args)
    {
    // reading AppSettings—————
    int pingIntervalMSec = Convert.ToInt32(ConfigurationManager.AppSettings[“pingIntervalMSec”].ToString());
    Console.WriteLine(“pingIntervalMSec: {0}”, pingIntervalMSec.ToString());
    //——————-
    // reading specific custom section in custom group————–
    NameValueCollection serverSection = (NameValueCollection)ConfigurationManager.GetSection(@”Servers/mrprodapp01″);
    if (serverSection != null)
    {
    foreach (string key in serverSection)
    {
    Console.WriteLine(“Section Key: {0}, {1}”, key, serverSection[key]);
    }
    }
    //—————————————–

    // — get specific custom group
    System.Configuration.Configuration configBB = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    ConfigurationSectionGroup serversGroup = configBB.SectionGroups.Get(“Servers”);

    Console.WriteLine(“Key value: {0}”, serversGroup.Sections.Count.ToString() );

    for(int cnt=0; cnt < serversGroup.Sections.Count; cnt++)
    {
    ConfigurationSection csTmp = serversGroup.Sections[cnt];
    serverSection = (NameValueCollection)ConfigurationManager.GetSection("Servers/" + csTmp.SectionInformation.Name);
    if (serverSection != null)
    {
    foreach (string key in serverSection)
    {
    Console.WriteLine("Section Key: Servers/{0} {1}, {2}", csTmp.SectionInformation.Name, key, serverSection[key]);
    }
    }
    }

    Console.WriteLine("————– LOOPING ALL —————");
    //——— loop over all custom groups ——————————

    ConfigurationSectionGroupCollection groups = configBB.SectionGroups;
    foreach (System.Configuration.ConfigurationSectionGroup grr in groups)
    {

    if (grr.Name.StartsWith("System.", StringComparison.OrdinalIgnoreCase)) continue;
    // if (!grr.IsDeclared)
    // continue;

    ConfigurationSectionGroup serversGroupA = configBB.SectionGroups.Get(grr.Name);

    for (int cnt = 0; cnt < serversGroupA.Sections.Count; cnt++)
    {
    ConfigurationSection csTmp = serversGroup.Sections[cnt];
    serverSection = (NameValueCollection)ConfigurationManager.GetSection(grr.Name + "/" + csTmp.SectionInformation.Name);
    if (serverSection != null)
    {
    foreach (string key in serverSection)
    {
    Console.WriteLine("Section Key: {0} {1}, {2}", grr.Name + "/" +csTmp.SectionInformation.Name, key, serverSection[key]);
    }
    }
    }

    }

    }
    }
    }

  5. Hi. Thank you for this tutorial! I have it all working but was wondering how can you add/save a message(eg: create a new in the config file) and also remove/delete a message during program execution? Thanks.

  6. Hi ash..

    You can do this, change the MessageElementCollection to KeyValueConfigurationCollection, and add the type as KeyValueConfigurationElement instead of MessageElement
    [ConfigurationCollection(typeof(KeyValueConfigurationElement), AddItemName = “Message”,
    CollectionType = ConfigurationElementCollectionType.BasicMap)]

    Now in the configuration (messages element) change the “name”, “value” to “key” “value” (this is not necessary if you can explicitly provide it)

    Sample:

    Now use this code to add a new key value to the config section:

    // Open App.Config of executable
    System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

    ((MyConfiguration)config.Sections[“MyConfiguration”]).Messages.Add(“Tweet”, “Tweet”);
    // Save the configuration file.
    config.Save(ConfigurationSaveMode.Modified);
    // Force a reload of a changed section.
    ConfigurationManager.RefreshSection(“MyConfiguration”);
    MyConfig = GetConfiguration();

    Console.WriteLine(“Message Tweet: ” + MyConfig.Messages[“Tweet”].Value);

    Similarly for removing use Remove() method instead of Add()

    Let me know if it works for you…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s