Skip to main content

Custom configuration in Web.config

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
  1. <?xml version="1.0"?>
  2. <configuration>
  3.   <connectionStrings>
  4.   </connectionStrings>
  5.   <system.web>
  6.     <compilation debug="true" targetFramework="4.0" />
  7.   </system.web>
  8. </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.
Code Snippet
  1. <?xml version="1.0"?>
  2. <configuration>
  3.   <appSettings>
  4.     <add key="some_int" value="1"/>
  5.   </appSettings>
  6. </configuration>
BAD CODE. Do not use.
Code Snippet
  1. protected void Page_Load(object sender, EventArgs e)
  2.     {
  3.         int i = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["some_int"]);
  4.     }

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.
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.

Code Snippet
  1. public class EmailConfiguration : ConfigurationElement
  2. {
  3.     [ConfigurationProperty("emailMessageEnabled"), SettingsDescription("Enabled of sending sended emails.")]
  4.     public bool EmailMessageEnabled
  5.     {
  6.         get { return (bool)this["emailMessageEnabled"]; }
  7.     }
  8.  
  9.     [ConfigurationProperty("timeOut", DefaultValue = 60), SettingsDescription("Default value of timeout.")]
  10.     public int TimeOut
  11.     {
  12.         get { return (int)this["timeOut"]; }
  13.     }
  14.  
  15.     [ConfigurationProperty("provider", DefaultValue = "Custom"), SettingsDescription("Mechanism for email sending.")]
  16.     public string DefaultProvider
  17.     {
  18.         get { return (string)this["provider"]; }
  19.     }
  20.  
  21. }

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
  1. <mailConfiguration emailMessageEnabled="truetimeOut="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
  1. public class CustomEmailConfiguration : ConfigurationElement
  2.     {
  3.         [ConfigurationProperty("customLogin"), SettingsDescription("Login to mail server.")]
  4.         public string Login { get { return (string)this["customLogin"]; } }
  5.  
  6.         [ConfigurationProperty("customPassword"), SettingsDescription("Password to mail server.")]
  7.         public string Password { get { return (string)this["customPassword"]; } }
  8.  
  9.         [ConfigurationProperty("customHost"), SettingsDescription("Address of mail server.")]
  10.         public string Url { get { return (string)this["customHost"]; } }
  11.  
  12.         [ConfigurationProperty("customSenderEmail"), SettingsDescription("Email of sender.")]
  13.         public string CustomSenderEmail { get { return (string)this["customSenderEmail"]; } }
  14.     }

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
  1.   <mailConfiguration emailMessageEnabled="true"timeOut="1000" provider="Custom">
  2.   <Custom customLogin="damian.zapart" customPassword="blogger123" customSenderEmail="damian.zapart@gmail.com" customHost="mail.google.pl">
  3.   </Custom>
  4. </mailConfiguration>

Code Snippet
  1. [ConfigurationProperty("Custom", IsRequired = false), SettingsDescription("Custom email sender configuration.")]
  2. 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
  1. public class RootSection : ConfigurationSection
  2.     {
  3.         [ConfigurationProperty("mailConfiguration"), SettingsDescription("Email configuration")]
  4.         public EmailConfiguration EmailConfiguration
  5.         {
  6.             get { return (EmailConfiguration)this["mailConfiguration"]; }
  7.         }
  8.     }

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
  1. public class SystemConfiguration
  2.     {
  3.         private static RootSection rootSection;
  4.         private static RootSection RootSection
  5.         {
  6.             get
  7.             {
  8.                 if (rootSection == null)
  9.                 {
  10.                     ConfigurationManager.RefreshSection("customConfiguration");
  11.                     object o = ConfigurationManager.GetSection("customConfiguration");
  12.                     rootSection = (RootSection)o;
  13.                 }
  14.                 return rootSection;
  15.             }
  16.         }
  17.  
  18.         public static EmailConfiguration EmailConfiguration
  19.         {
  20.             get
  21.             {
  22.                 return RootSection.EmailConfiguration;
  23.             }
  24.         }
  25.     }

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
  1. <?xml version="1.0"?>
  2. <configuration>
  3.   <configSections>
  4.     <section name="systemConfiguration" type="Common.RootSection, Common" />
  5.   </configSections>
  6.   <systemConfiguration>
  7.       <mailConfiguration emailMessageEnabled="true"timeOut="1000" provider="Custom">
  8.       <Custom customLogin="damian.zapart" customPassword="blogger123" customSenderEmail="damian.zapart@gmail.com" customHost="mail.google.pl">
  9.       </Custom>
  10.     </mailConfiguration>
  11.   </systemConfiguration>
  12. </configuration>

It done. Now we are able to use it.

Code Snippet
  1. if (Common.SystemConfiguration.EmailConfiguration.EmailMessageEnabled)
  2.          {
  3.              string senderEmail =  Common.SystemConfiguration.EmailConfiguration.CustomConfiguration.CustomSenderEmail;
  4.              //TODO: some logic for sending emails.
  5.          }

Full projects source code is free available here.

Thank you

Popular posts from this blog

Full-Text Search with PDF in Microsoft SQL Server

Last week I get interesting task to develop. The task was to search input text in PDF file stored in database as FileStream. The task implementation took me some time so I decided to share it with other developers. Here we are going to use SQL Server 2008 R2 (x64 Developers Edition), external driver from Adobe, Full-Text Search technology and FileStream technology.Because this sems a little bit comlicated let`s make this topic clear and do it step by step. 1) Enable FileStream - this part is pretty easy, just check wheter You already have enabled filestream on Your SQL Server instance - if no simply enable it as in the picture below. Picture 1. Enable filestream in SQL Server instance. 2) Create SQL table to store files  - mainly ther will be PDF file stored but some others is also be allright. Out table DocumentFile will be created in dbo schema and contain one column primary key with default value as sequential GUID. Important this is out table contains FileSt...

Persisting Enum in database with Entity Framework

Problem statement We all want to write clean code and follow best coding practices. This all engineers 'North Star' goal which in many cases can not be easily achievable because of many potential difficulties with converting our ideas/good practices into working solutions.  One of an example I recently came across was about using ASP.NET Core and Entity Framework 5 to store Enum values in a relational database (like Azure SQL). Why is this a problem you might ask... and my answer here is that you want to work with Enum types in your code but persist an integer in your databases. You can think about in that way. Why we use data types at all when everything could be just a string which is getting converted into a desirable type when needed. This 'all-string' approach is of course a huge anti-pattern and a bad practice for many reasons with few being: degraded performance, increased storage space, increased code duplication.  Pre-requirements 1. Status enum type definition...

Using Newtonsoft serializer in CosmosDB client

Problem In some scenarios engineers might want to use a custom JSON serializer for documents stored in CosmosDB.  Solution In CosmosDBV3 .NET Core API, when creating an instance of  CosmosClient one of optional setting in  CosmosClientOptions is to specify an instance of a Serializer . This serializer must be JSON based and be of  CosmosSerializer type. This means that if a custom serializer is needed this should inherit from CosmosSerializer abstract class and override its two methods for serializing and deserializing of an object. The challenge is that both methods from  CosmosSerializer are stream based and therefore might be not as easy to implement as engineers used to assume - still not super complex.  For demonstration purpose as or my custom serializer I'm going to use Netwonsoft.JSON library. Firstly a new type is needed and this must inherit from  CosmosSerializer.  using  Microsoft.Azure.Cosmos; using  Newtonsoft.Json; usin...