Click here to Skip to main content
15,906,816 members
Articles / Programming Languages / C#
Tip/Trick

Custom App Setting

Rate me:
Please Sign up or sign in to vote.
3.57/5 (4 votes)
21 Jan 2016CPOL3 min read 9.5K   111   8  
This tip describes how to better organize config settings and use them.

Introduction

How often we have seen settings defined in config file like this:

XML
<xml version="1.0" encoding="utf-8" ?>
	<configuration> 
		<appSettings> 
			<add key="mailserver" value="default"/>
			<add key="mailPort" value="800"/> 
			<add key="useSSL" value="true"/> 
			<add key ="from" value="abc@prowareness.com"/>
			<add key ="To1" value="recieverl@prowareness.com"/> 
			<add key ="TolDescription" value="stakeholder"/>
			<add key ="To2" value="reciever2@prowareness.com"/>
			<add key ="To2Description" value="security admin"/> 
			<add key ="To3" value="reciever3@prowareness.com"/>
			<add key ="To3Description" value="ops team head"/>
			<!-- and many other settings--> 
		</appSettings>

The problem starts when there are a lot and there is no organized/logical way of putting them in config file and reading in application. The more organized way of implementing custom app setting is via custom configSection. Let’s first have a glimpse of config file to know what we want to achieve.

XML
<settings>
    <mailSetting>
      <name>default</name>
      <port>800</port>
      <usessl>true</usessl>
      <from>abc@prowareness.com</from>
      <description companyName="Prowareness" />
      <to>
        <email description="stakeholder" value="reciever1@prowareness.com"/>
        <email description="security admin" value="reciever2@prowareness.com"/>
        <email description="ops team head" value="reciever3@prowareness.com"/>
      </to>
    </mailSetting>
  </settings>

As it’s apparent now, we have organized our mail server related settings in a more organized way. Now by creating a strongly typed class for these settings, we can read these settings directly by using our custom type. How? Let’s understand this process step by step.

Step 1

First, we need to create a custom SettingsConfiguration class or any name you like inherited from IConfigurationSectionHandler interface. This is the class which we will use to register our custom config section in config file. For now, just add a method called Create as shown below. We will discuss rest of the code in subsequent steps.

C#
using System.Collections.Generic;
using System.Configuration;
using System.Xml;
using System.Xml.Serialization;

namespace CustomAppSettingsEx.Configuration
{
    public sealed class SettingsConfiguration : IConfigurationSectionHandler
    {
        public MailSettingElement Read()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(MailSettingElement));
            var sectionNode = (XmlNode)ConfigurationManager.GetSection("settings");
            MailSettingElement mailSetting;
            if (sectionNode != null)
            {
                XmlNode mailSettingNode = sectionNode.SelectSingleNode("mailSetting");
                var reader = new XmlNodeReader(mailSettingNode);
                mailSetting = (MailSettingElement)serializer.Deserialize(reader);
            }
            else
            {
                return new MailSettingElement
                {
                    Name = "default.mail.smtp",
                    Port = 8080,
                    UseSsl = true,
                    From="abc@prowareness.nl",
                    Description=new Description(){CompanyName="Prowareness"},
                    To = new To() { Email = new List<Email>()
                    {
                        new Email() { Description = "stakeholder", 
                        Value = "reciever1@prowareness.com" },
                        new Email() { Description = "security admin", 
                        Value = "reciever2@prowareness.com" },
                        new Email() { Description = "ops team head", 
                        Value = "reciever3@prowareness.com" },
                    } }
                    
                };
            }
            return mailSetting;
        }

        public object Create(object parent, object configContext, XmlNode section)
        {
            return section;
        }
    }
}

Step 2

Now, we will register this type into our config file. How? Here is the way:

XML
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="settings" type="CustomAppSettingsEx.Configuration.SettingsConfiguration, 
	CustomAppSettingsEx"  />
  </configSections>

Here the name "settings" should be the same as defined in our mailSetting parent node. In type first argument is our config class name with full namespace and the second parameter is the assembly name in which this config class is defined.

XML
<settings>
    <mailSetting>

So, our basic config skeleton is ready. Let’s fill it with mailSetting that we want to achieve.

Step 3

As seen earlier, we already have our mailSetting defined in config file and to read it via XmlSerializer, we need to create mapping classes for each node [if node does contain any other node(s)]. So here are our classes.

C#
using System.Collections.Generic;
using System.Xml.Serialization;

namespace CustomAppSettingsEx.Configuration
{
    [XmlRoot("mailSetting")]
    public class MailSettingElement
    {
        [XmlElement("name")]
        public string Name { get; set; }

        [XmlElement("port")]
        public int Port { get; set; }

        [XmlElement("usessl")]
        public bool UseSsl { get; set; }

        [XmlElement("description")]
        public Description Description { get; set; }

        [XmlElement("from")]
        public string From { get; set; }

        [XmlElement("to")]
        public To To { get; set; }
    }

    [XmlRoot("description")]
    public class Description
    {
        [XmlAttribute("companyName")]
        public string CompanyName { get; set; }
    }

    [XmlRoot("to")]
    public class To
    {
        [XmlElement("email")]
        public List<Email> Email { get; set; }
    }

    [XmlRoot("email")]
    public class Email
    {
        [XmlAttribute("description")]
        public string Description { get; set; }

        [XmlAttribute("value")]
        public string Value { get; set; }
    }
}

Here, we can have a different class name, but name defined in XmlRoot should match with what is defined in config file. Now, there are different ways of defining classes and their members based on their representation.

  1. <name>default</name>: In this case, we can have a property defined decorated with attribute XmlElement where name in attribute should match with name defined in config file.
  2. <description companyName="Prowareness" />: For mapping this kind of node, we need to have a separate class where CompanyName will be decorated with attribute XmlAttribute instead of XmlElement.
    [XmlRoot("description")]
        public class Description
        {
            [XmlAttribute("companyName")]
            public string CompanyName { get; set; }
        }
  3. Now, it's a little tricky if we want to define an email collection like this in our config file.
    <to>
            <email description="stakeholder" value="reciever1@prowareness.com"/>
    	<email description="security admin" value="reciever2@prowareness.com"/>     
          </to>

    Here is the solution:

    [XmlRoot("to")]
        public class To
        {
            [XmlElement("email")]
            public List<Email> Email { get; set; }
        }
    
        [XmlRoot("email")]
        public class Email
        {
            [XmlAttribute("description")]
            public string Description { get; set; }
    
            [XmlAttribute("value")]
            public string Value { get; set; }
        }

    In this case, List<Email> will be our XmlElement and the Email class can be defined as above. So now, all our the mapping classes are ready. Let’s see how to read it in our application.

Step 4

Now, let’s go back to our class SettingsConfiguration’s Read method.

C#
public MailSettingElement Read()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(MailSettingElement));
            var sectionNode = (XmlNode)ConfigurationManager.GetSection("settings");
            MailSettingElement mailSetting;
            if (sectionNode != null)
            {
                XmlNode mailSettingNode = sectionNode.SelectSingleNode("mailSetting");
                var reader = new XmlNodeReader(mailSettingNode);
                mailSetting = (MailSettingElement)serializer.Deserialize(reader);
            }
            else
            {
                return new MailSettingElement
                {
                    Name = "default.mail.smtp",
                    Port = 8080,
                    UseSsl = true,
                    From="abc@prowareness.nl",
                    Description=new Description(){CompanyName="Prowareness"},
                    To = new To() { Email = new List<Email>()
                    {
                        new Email() { Description = "stakeholder", 
                        Value = "reciever1@prowareness.com" },
                        new Email() { Description = "security admin", 
                        Value = "reciever2@prowareness.com" },
                        new Email() { Description = "ops team head", 
                        Value = "reciever3@prowareness.com" },
                    } }
                    
                };
            }
            return mailSetting;
        }

Here, we are reading our custom config section named "settings" which we registered in step 2. We get our "mailSetting" node using SelectSingleNode method and then using XmlSerializer we deserialize it to our MailSettingElement class. And then, now onwards, we can use this class to get the settings from config file and apply wherever needed.

So what advantages am I’m getting out of this?

As we can see, even if sectionNode is null in the above code (meaning you missed defining settings in config file due to various reasons, may be you are not sure it’s value in development or test environment), you can still pass some default value in code as shown above.

By using this approach, we can logically organize our settings in different sections which can be reused across applications if this class is developed as an assembly.

And here is the way to read/use these settings:

C#
private static void CustomSettings()
       {
           SettingsConfiguration setting = new SettingsConfiguration();
           MailSettingElement mailSetting = setting.Read();

           //Use mailSetting read from config file
           string mailTo = string.Empty;
           for(int i=0; i< mailSetting.To.Email.Count;i++)
           {
               if (i != 0)
                   mailTo += ",";
               mailTo += mailSetting.To.Email[i].Value;
           }
           MailMessage mailMessage = new MailMessage(mailSetting.From, mailTo, "custom subject",
               "mail body");

           SmtpClient mailClient=new SmtpClient(mailSetting.Name);
           mailClient.EnableSsl = mailSetting.UseSsl;
           mailClient.Port = mailSetting.Port;

           try
           {
               mailClient.Send(mailMessage);
           }
           catch (Exception)
           {
               //Handle here
           }

           #region Read All
           Console.WriteLine("<mailSetting>");
           Console.WriteLine("\t<name>{0}</name>", mailSetting.Name);
           Console.WriteLine("\t<port>{0}</port>", mailSetting.Port);
           Console.WriteLine("\t<usessl>{0}</usessl>", mailSetting.UseSsl);
           Console.WriteLine("\t<from>{0}</from>", mailSetting.From);
           Console.WriteLine("\t<description companyName = \"{0}\"/>",
           mailSetting.Description.CompanyName);
           Console.WriteLine("\t<to>");
           foreach (Email email in mailSetting.To.Email)
           {
               Console.WriteLine("\t\t<email description = \"{0}\"
               value = \"{1}\"/>", email.Description, email.Value);
           }
           Console.WriteLine("\t</to>");
           Console.WriteLine("</mailSetting>");
           #endregion
           Console.ReadLine();
       }

That’s all folks for this tip. Please let me know your views/comments.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
India India
Passionate and self starter professional having wide experience in C# 8, .Net Core, Web API, Entity framework core, Rabbit MQ, LINQ, , SQL server 2016, Azure ( service bus, app services, function, micro service, container, cosmos db, storage account) , java script.
Linkedin profile: https://www.linkedin.com/in/mahesh-pratap-8248118/

Comments and Discussions

 
-- There are no messages in this forum --