Almost all projects have come configuration which is mostly unique and absolutely custom. That configuration allow to customize some application behaviors, settings and switching some parts of implementation. In Microsoft .NET Framework configuration can be stored in two types of files:
- *.config - XML based file which differ depending on project type. In web application such ASP.NET or WCF we working with Web.config file and in desktop application use App.config.
- *.setting - deprecated type of file which allow to store custom settings in XML based, strongly typed file.
In this post I focus on Web.config file and demonstrate how to create custom section for this type of file.
At the beginning we need some basic configuration file which is easy to get one because after we created new ASP.NET project from a template we already should have one. Simplified version on Web.config looks like as resented below.
Code Snippet
- <?xml version="1.0"?>
- <configuration>
- <connectionStrings>
- </connectionStrings>
- <system.web>
- <compilation debug="true" targetFramework="4.0" />
- </system.web>
- </configuration>
On code snippet we can see some initial XML tags including XML declaration (line 1) and configuration tag which is a root node. Because meaning of connectionString and compilation node is quite obvious I not going to focus about its and if you want you can read more about its here. Additionally I want to notice that many developers have very bad habit and put custom configuration settings in appSettings section in inside configuration section. I understand that in many project there are very short deadline but such solution is not correct and also is not consistent with good practices. Because developers put all custom settings in a NameValueCollection type there is no strongly type definition for each single setting and this may cause problem and its very hard to maintenance. As I mentioned before such approach does not guarantee that type of value stored in setting will be correct that is why additional implementation of type checking and handling exceptions is required in this case.
BAD CODE. Do not use.
BAD CODE. Do not use.
Now it`s time for a good news. We can write our own custom sections on Web.config which is strongly typed, contains default values and are required or not. Let's start with creating a new DLL project named Common (picture 1.) and (which is very important) add reference to System.Configuration namespace. Also remember to add reference to a new project to our ASP.NET application.
Simply solution architecture is done already so now its time for some implementation. Let`s assume that we want to create custom setting for email sending. We need to introduce some basic configuration parameters for SMTP server as well as some default email provider such .NET SMTPClient class. First of all we create class for accessing information about email general settings such: mechanism enabled, default timeout, etc.
To make such implementation we must derive from ConfigurationElement class.
As you can see on the code snippet above I added some custom properties and all of them is strongly typed. Also all have ConfigurationProperty attribute which allow to set following properties for each setting:
This no everything because still there is no configuration for custom email sender mechanism. To make such implementation I add new class CustomEmailConfiguration which is defined as presented below.
Is stores information about login, password and so on. So now all I need to to is put it inside my mail configuration node (see below) and because I neested this part iniside other, additionally I should add new property to the EmailConfiguration class.
My new, custom configuration section for emails is ready so now it is time to start using it. First of all I want to locate it inside more generic section just to put all my further configuration elements in one section. To do that I need to create one more class named RootSection presented below. Thank such approach in the future if I will need to add an other custom configuration element I just put it as new property of RootSection section with new type.
Whole solution is almost ready. We have created general email settings in EmailConfiguration, define CustomEmailConfiguration inside it and also create RootSection which will store all our custom configuration settings. Now it`s time to create wrapper class for accessing its. Presented below SystemConfiguration class is a classic example of the singleton design pattern. In static properties I refreshing my custom section by using ConfigurationManager .NET Framework class and than get this section as type of my RootSection. Due to my RootSection contains property of type EmailConfiguration I can simply navigate to it and all it properties.
For now, project compile well but we still can`t use our configuration because we not register our new section. Registering section is process of mapping XML node to .NET framework type. This is very easy and all I need to do is put three lines in Web.config (line 3,4 and 5).
It done. Now we are able to use it.
Thank you
BAD CODE. Do not use.
Code Snippet
- <?xml version="1.0"?>
- <configuration>
- <appSettings>
- <add key="some_int" value="1"/>
- </appSettings>
- </configuration>
Code Snippet
- protected void Page_Load(object sender, EventArgs e)
- {
- int i = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["some_int"]);
- }
Now it`s time for a good news. We can write our own custom sections on Web.config which is strongly typed, contains default values and are required or not. Let's start with creating a new DLL project named Common (picture 1.) and (which is very important) add reference to System.Configuration namespace. Also remember to add reference to a new project to our ASP.NET application.
Picture 1. Solution overview. |
To make such implementation we must derive from ConfigurationElement class.
Code Snippet
- public class EmailConfiguration : ConfigurationElement
- {
- [ConfigurationProperty("emailMessageEnabled"), SettingsDescription("Enabled of sending sended emails.")]
- public bool EmailMessageEnabled
- {
- get { return (bool)this["emailMessageEnabled"]; }
- }
- [ConfigurationProperty("timeOut", DefaultValue = 60), SettingsDescription("Default value of timeout.")]
- public int TimeOut
- {
- get { return (int)this["timeOut"]; }
- }
- [ConfigurationProperty("provider", DefaultValue = "Custom"), SettingsDescription("Mechanism for email sending.")]
- public string DefaultProvider
- {
- get { return (string)this["provider"]; }
- }
- }
As you can see on the code snippet above I added some custom properties and all of them is strongly typed. Also all have ConfigurationProperty attribute which allow to set following properties for each setting:
- Name: name of configuration element in Web.config
- DefaultValue: determine default value as object with null as default
- IsRequired: section is required or can me omitted
- IsDefaultCollection: determine property as default collection of settings (this value is ignored if the property is not a collection)
Inside each properties I`m accessing to the configuration attribute and convert it to coreesponding type. In Web.config this class is represented as following XML.
Code Snippet
- <mailConfiguration emailMessageEnabled="true" timeOut="1000" provider="Custom"></mailConfiguration>
This no everything because still there is no configuration for custom email sender mechanism. To make such implementation I add new class CustomEmailConfiguration which is defined as presented below.
Code Snippet
- public class CustomEmailConfiguration : ConfigurationElement
- {
- [ConfigurationProperty("customLogin"), SettingsDescription("Login to mail server.")]
- public string Login { get { return (string)this["customLogin"]; } }
- [ConfigurationProperty("customPassword"), SettingsDescription("Password to mail server.")]
- public string Password { get { return (string)this["customPassword"]; } }
- [ConfigurationProperty("customHost"), SettingsDescription("Address of mail server.")]
- public string Url { get { return (string)this["customHost"]; } }
- [ConfigurationProperty("customSenderEmail"), SettingsDescription("Email of sender.")]
- public string CustomSenderEmail { get { return (string)this["customSenderEmail"]; } }
- }
Is stores information about login, password and so on. So now all I need to to is put it inside my mail configuration node (see below) and because I neested this part iniside other, additionally I should add new property to the EmailConfiguration class.
Code Snippet
- <mailConfiguration emailMessageEnabled="true"timeOut="1000" provider="Custom">
- <Custom customLogin="damian.zapart" customPassword="blogger123" customSenderEmail="damian.zapart@gmail.com" customHost="mail.google.pl">
- </Custom>
- </mailConfiguration>
Code Snippet
- [ConfigurationProperty("Custom", IsRequired = false), SettingsDescription("Custom email sender configuration.")]
- public CustomEmailConfiguration CustomConfiguration { get { return (CustomEmailConfiguration)this["Custom"]; } }
My new, custom configuration section for emails is ready so now it is time to start using it. First of all I want to locate it inside more generic section just to put all my further configuration elements in one section. To do that I need to create one more class named RootSection presented below. Thank such approach in the future if I will need to add an other custom configuration element I just put it as new property of RootSection section with new type.
Code Snippet
- public class RootSection : ConfigurationSection
- {
- [ConfigurationProperty("mailConfiguration"), SettingsDescription("Email configuration")]
- public EmailConfiguration EmailConfiguration
- {
- get { return (EmailConfiguration)this["mailConfiguration"]; }
- }
- }
Whole solution is almost ready. We have created general email settings in EmailConfiguration, define CustomEmailConfiguration inside it and also create RootSection which will store all our custom configuration settings. Now it`s time to create wrapper class for accessing its. Presented below SystemConfiguration class is a classic example of the singleton design pattern. In static properties I refreshing my custom section by using ConfigurationManager .NET Framework class and than get this section as type of my RootSection. Due to my RootSection contains property of type EmailConfiguration I can simply navigate to it and all it properties.
Code Snippet
- public class SystemConfiguration
- {
- private static RootSection rootSection;
- private static RootSection RootSection
- {
- get
- {
- if (rootSection == null)
- {
- ConfigurationManager.RefreshSection("customConfiguration");
- object o = ConfigurationManager.GetSection("customConfiguration");
- rootSection = (RootSection)o;
- }
- return rootSection;
- }
- }
- public static EmailConfiguration EmailConfiguration
- {
- get
- {
- return RootSection.EmailConfiguration;
- }
- }
- }
For now, project compile well but we still can`t use our configuration because we not register our new section. Registering section is process of mapping XML node to .NET framework type. This is very easy and all I need to do is put three lines in Web.config (line 3,4 and 5).
Code Snippet
- <?xml version="1.0"?>
- <configuration>
- <configSections>
- <section name="systemConfiguration" type="Common.RootSection, Common" />
- </configSections>
- <systemConfiguration>
- <mailConfiguration emailMessageEnabled="true"timeOut="1000" provider="Custom">
- <Custom customLogin="damian.zapart" customPassword="blogger123" customSenderEmail="damian.zapart@gmail.com" customHost="mail.google.pl">
- </Custom>
- </mailConfiguration>
- </systemConfiguration>
- </configuration>
It done. Now we are able to use it.
Code Snippet
- if (Common.SystemConfiguration.EmailConfiguration.EmailMessageEnabled)
- {
- string senderEmail = Common.SystemConfiguration.EmailConfiguration.CustomConfiguration.CustomSenderEmail;
- //TODO: some logic for sending emails.
- }