• Dynamic Loading of Custom Configuration XML using Reflection in C#

    Posted on March 31, 2012 by in C#, Dotnet

    We store application settings or other entries which need to be configured from time to time in configuration files. It may be an app.config or web.config or custom config. For a simple application, we would use simple add key/value settings but it would not suffice for complex applications. So we end up creating our own configuration files, which we update on need-to-need basis. We might use .NET Configuration Manager or XML Reader to parse the configuration file and load settings. I have noticed that in most of the applications, configuration loading logic has be to changed when new entry is added or existing type is changed. In this article, I would be explaining how to make the loading logic dynamic enough so that we could keep changes to a bare minimum.

    Simple Configuration XML(Config.xml):

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <LogFilePath>C:\Temp\log.txt</LogFilePath>
      <ConnString>Data Source=SERVERNAME;Initial Catalog= 
    TEST;User=testuser;Password=testuser;
      </ConnString>
      <ServiceTimeout>10000</ServiceTimeout>
      <ServiceUrl>http://localhost/TestService/Service.cs</ServiceUrl>
    </configuration>

    I have created a custom attribute ConfigurationAttribue to store the mapping name of the property. We use this mapping name to match it with corresponding entry in config xml.

    public class ConfigurationAttribute : Attribute
        {
            private string keyName;        
            public ConfigurationAttribute(string sKeyName)
            {
                this.keyName = sKeyName;
            }
        }

    In order to store this configuration details, created a simple configuration class (CustomConfiguration.cs). As you can notice all the public properties are identified with Configuration Attribute and the mapping name associated with the attribute.

    private string logFilePath;
    [ConfigurationAttribute("LogFilePath")]
    public string ConfigLogFilePath
    {
        get{
            return logFilePath;
        }
        set{
            logFilePath = value;
        }
    }

    For fast matching of the properties and xml settings, we parse the configuration XML and populated into a dictionary object

    Dictionary<string, string> nodesHash = new Dictionary<string, string>();
        XmlDocument xmlDoc = new XmlDocument();
        ///Load the XMLdocument object with the xml file
        xmlDoc.Load(Path.Combine(Environment.CurrentDirectory, "Config.XML"));
        ///Fetch the root element
        XmlNode rootelement = xmlDoc.DocumentElement;    
        if (rootelement.ChildNodes!= null){
            foreach (XmlNode node in nodeList)
            {
                if (!nodesHash.ContainsKey(node.Name))
                    nodesHash.Add(node.Name, node.InnerText);
            }
        }
        return nodesHash;

    We don’t want to change our mapping logic whenever there is a new entry added to the configuration, so we use reflection and will get the list of all public properties available in custom configuration object which have mapping information defined.  The mapping name is our key to get the value from xml values list.

    PropertyInfo[] properties = obj.GetType().GetProperties();
    PropertyInfo oPropertyInfo = properties[0];
    configAttributes = 
    (ConfigurationAttribute[])oPropertyInfo.GetCustomAttributes( 
    Type.GetType("ConfigurationLoader.ConfigurationAttribute"), false);
    oPropertyInfo.SetValue(obj, Convert.ChangeType(
                        nodesHash[configAttribute.KeyName], 
      oPropertyInfo.PropertyType), null);

    The same configuration object should be shared across the application and we need to load it only once. So I have made my configuration objec t to follow singleton design pattern. So our config class would have only one instance, and a global point of access to the instance.

    public class CustomConfiguration
    {
    private static CustomConfiguration configInstance;
    private static object sync = new object();
     private CustomConfiguration()
            { }
    public static void Initialize()
            {
                if (configInstance == null){
                    lock (sync){
                        configInstance = new CustomConfiguration();                    
                    }
                }
            }
     }

    Source Code:

    Program.cs

    public class Program
    {
        public static void Main(string[] args)
        {
            //load configuration
            CustomConfiguration.Initialize();
            Console.WriteLine("LogFilePath:     " + 
    CustomConfiguration.LogFilePath);
            Console.WriteLine("ConnString:     " + 
    CustomConfiguration.ConnString);
            Console.WriteLine("ServiceUrl:     " + 
    CustomConfiguration.ServiceUrl);
            Console.WriteLine("ServiceTimeout:     " + 
    CustomConfiguration.ServiceTimeout.ToString());
            Console.ReadLine();
        }
    }

    CustomConfiguration.cs

    public class CustomConfiguration
    {
        private static CustomConfiguration configInstance;
        private static object sync = new object();
    
        private string logFilePath;
        private string connString;
        private long serviceTimeout;
        private string serviceUrl;
    
        private CustomConfiguration()
        { }
    
        public static void Initialize()
        {
            if (configInstance == null)&nbsp;{
                lock (sync)&nbsp;{
                    configInstance = new CustomConfiguration();
                    ConfigLoader loader = new ConfigLoader();
                    loader.LoadConfiguration(configInstance);
                }
            }
        }
        public static string LogFilePath
        {
            get{
                return configInstance.ConfigLogFilePath;
            }
        }
        public static string ConnString
        {
            get{
                return configInstance.ConfigConnString;
            }
        }
        public static string ServiceUrl
        {
            get{
                return configInstance.ConfigServiceUrl;
            }
        }
        public static long ServiceTimeout
        {
            get&nbsp;{
                return configInstance.ConfigServiceTimeout;
            }
        }
    
        [ConfigurationAttribute("LogFilePath")]
        public string ConfigLogFilePath
        {
            get{
                return logFilePath;
            }
            set{
                logFilePath = value;
            }
        }
        [ConfigurationAttribute("ConnString")]
        public string ConfigConnString
        {
            get{
                return connString;
            }
            set{
                connString = value;
            }
        }
        [ConfigurationAttribute("ServiceTimeout")]
        public long ConfigServiceTimeout
        {
            get{
                return serviceTimeout;
            }
            set{
                serviceTimeout = value;
            }
        }
        [ConfigurationAttribute("ServiceUrl")]
        public string ConfigServiceUrl
        {
            get{
                return serviceUrl;
            }
            set{
                serviceUrl = value;
            }
        }
    }

    ConfigLoader.cs

    public class ConfigLoader
    {
    public void LoadConfiguration(CustomConfiguration customConfig)
    {
        LoadFromXML(customConfig);
    }
    private void LoadFromXML(object obj)
    {
        PropertyInfo[] properties = obj.GetType().GetProperties();
        ConfigurationAttribute[] configAttributes = null;
        ConfigurationAttribute configAttribute;
        //parse and load the config xml nodes and values into dictionary
        Dictionary<string, string> nodesHash = ParseConfigXML();
        ///Iterate through configuration properties
        foreach (PropertyInfo oPropertyInfo in properties){
            configAttributes=(ConfigurationAttribute[])oPropertyInfo.
                             GetCustomAttributes(Type.GetType( 
                             "ConfigurationLoader.ConfigurationAttribute"), 
                             false);
            if (((configAttributes != null)) & configAttributes.Length > 0){
                configAttribute = configAttributes[0];
                //Get the corresponding attribute value from the 
                //XML node list and set it
                if (nodesHash.ContainsKey(configAttribute.KeyName))
                    oPropertyInfo.SetValue(obj, 
                                   Convert.ChangeType(
                                   nodesHash[configAttribute.KeyName], 
                                   oPropertyInfo.PropertyType)
                                   , null);
            }
        }
    }
    private Dictionary<string, string> ParseConfigXML()
    {
        Dictionary<string, string> nodesHash = new Dictionary<string, string>();
        XmlNodeList nodeList;
        XmlDocument xmlDoc = new XmlDocument();
        ///Load the XMLdocument object with the xml file&nbsp; 
        xmlDoc.Load(Path.Combine(Environment.CurrentDirectory, "Config.XML"));    
        nodeList = xmlDoc.DocumentElement.ChildNodes;
        if (nodeList != null){
            foreach (XmlNode node in nodeList)
            {
                if (!nodesHash.ContainsKey(node.Name))
                    nodesHash.Add(node.Name, node.InnerText);
            }
        }
        return nodesHash;
    }
    }

    ConfigurationAttribute.cs

    public class ConfigurationAttribute : Attribute
    {
        private string keyName;
        public string KeyName
        {
            set{
                keyName = value;
            }
            get{
                return keyName;
            }
        }
        public ConfigurationAttribute(string sKeyName)
        {
            this.keyName = sKeyName;
        }
    }
    
    Be Sociable, Share!
      Post Tagged with , , ,

    Written by

    Software architect with over 10 years of proven experience in designing & developing n-tier and web based software applications, for Finance, Telecommunication, Manufacturing, Internet and other Commercial industries. He believes that success depends on one's ability to integrate multiple technologies to solve a simple as well as complicated problem.

    View all articles by

    Email : [email protected]

    Leave a Reply