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?
- <?xml version=“1.0“ encoding=“utf-8“ ?>
- <configuration>
- <configSections>
- <section name=“MyConfiguration“ type=“ConfigurationC.MyConfiguration, ConfigurationC“/>
- </configSections>
- <!– Configuration settings for MyConfiguration Starts –>
- <MyConfiguration
- To=“support@nnish.com“
- From=“sales@nnish.com“>
- <Messages>
- <Message Name=“Email“ Value=“email“/>
- <Message Name=“Sms“ Value=“sms“/>
- <Message Name=“Tweet“ Value=“tweet“ />
- </Messages>
- </MyConfiguration>
- <!– Configuration settings for MyConfiguration Ends –>
- </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
- using System.Configuration;
- namespace ConfigurationC
- {
- /// <summary>
- /// Class holds the <Message> element
- /// </summary>
- public class MessageElement : ConfigurationElement
- {
- // Holds the Name attribute of the Message
- private static readonly ConfigurationProperty messageName =
- new ConfigurationProperty(“Name”, typeof(string), string.Empty, ConfigurationPropertyOptions.IsRequired);
- // Holds the Value attribute value of Message.
- private static readonly ConfigurationProperty messageValue =
- new ConfigurationProperty(“Value”, typeof(string), string.Empty, ConfigurationPropertyOptions.IsRequired);
- public MessageElement()
- {
- base.Properties.Add(messageName);
- base.Properties.Add(messageValue);
- }
- /// <summary>
- /// Name
- /// </summary>
- [ConfigurationProperty("Name", IsRequired = true)]
- public string Name
- {
- get { return (string)this[messageName]; }
- }
- /// <summary>
- /// Value
- /// </summary>
- [ConfigurationProperty("Value", IsRequired = true)]
- public string Value
- {
- get { return (string)this[messageValue]; }
- }
- }
- }
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
- using System.Configuration;
- namespace ConfigurationC
- {
- [ConfigurationCollection(typeof(MessageElement), AddItemName = "Message",
- CollectionType = ConfigurationElementCollectionType.BasicMap)]
- public class MessageCollection : ConfigurationElementCollection
- {
- protected override ConfigurationElement CreateNewElement()
- {
- return new MessageElement();
- }
- protected override object GetElementKey(ConfigurationElement element)
- {
- return ((MessageElement)element).Name;
- }
- new public MessageElement this[string name]
- {
- get { return (MessageElement)BaseGet(name); }
- }
- }
- }
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 ])
- using System.Configuration;
- namespace ConfigurationC
- {
- public class MyConfiguration : ConfigurationSection
- {
- private static readonly ConfigurationProperty toAttribute =
- new ConfigurationProperty(“To”, typeof(string), string.Empty, ConfigurationPropertyOptions.IsRequired);
- private static readonly ConfigurationProperty fromAttribute =
- new ConfigurationProperty(“From”, typeof(string), string.Empty, ConfigurationPropertyOptions.IsRequired);
- private static readonly ConfigurationProperty messagesElement =
- new ConfigurationProperty(“Messages”, typeof(MessageCollection), null, ConfigurationPropertyOptions.IsRequired);
- public MyConfiguration()
- {
- base.Properties.Add(toAttribute);
- base.Properties.Add(fromAttribute);
- base.Properties.Add(messagesElement);
- }
- /// <summary>
- /// To
- /// </summary>
- [ConfigurationProperty("To", IsRequired = true)]
- public string To
- {
- get { return (string)this[toAttribute]; }
- }
- /// <summary>
- /// From
- /// </summary>
- [ConfigurationProperty("From", IsRequired = true)]
- public string From
- {
- get { return (string)this[fromAttribute]; }
- }
- /// <summary>
- /// Messages Collection
- /// </summary>
- [ConfigurationProperty("Messages", IsRequired = true)]
- public MessageCollection Messages
- {
- get { return (MessageCollection)this[messagesElement]; }
- }
- }
- }
Explanation:
- Add all the Attributes and Elements as the ConfigurationProperty of corresponding type.
And the Program.cs
- using System;
- using System.Configuration;
- namespace ConfigurationC
- {
- public class Program
- {
- public static MyConfiguration MyConfig;
- static void Main(string[] args)
- {
- MyConfig = GetConfiguration();
- Console.WriteLine(“This program comes with no warranty!”);
- Console.WriteLine(“Values found in the configuration”);
- Console.WriteLine(“To: “ + MyConfig.To);
- Console.WriteLine(“From: “ + MyConfig.From);
- Console.WriteLine(“Message Email: “ + MyConfig.Messages["Email"].Value);
- Console.WriteLine(“Message Sme: “ + MyConfig.Messages["Sms"].Value);
- Console.WriteLine(“Message Tweet: “ + MyConfig.Messages["Tweet"].Value);
- Console.ReadLine();
- }
- private static MyConfiguration GetConfiguration()
- {
- return (MyConfiguration)ConfigurationManager.GetSection(“MyConfiguration”);
- }
- }
- }
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.
Cheers!
Google Profile
Good One… Keep on posting
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?
@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.
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?
What can I say? I can’t tell the difference between a section and an element collection! All fixed.
Glad that yours got fixed.
You can download my sample and tweak it for your need. It works with VS 2010.
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]);
}
}
}
}
}
}
}