Saturday, October 8, 2016

C# : managing global configurations with XML serializer

Managing configurations within any non-trivial program can easily turn to be a tricky issue. One might have a lot of different types of configuration parameters, which might need to be used as input parameters for several different classes. Moreover, there might be a need for using different set of configurations and one should be able to switch to another set as easily as possible.

This time I wanted to present a scheme, what I have been using for hosting global configuration parameters in some of my applications. Presented scheme solves three following issues :
  1. Configuration set used by program executable can easily be changed to another set
  2. There is no need to touch App.Config file
  3. All configuration properties are global, static and safe (read-only)
First of all, a set of configurations will be processed (read) from XML file using XML serializer. Assume I have following two configuration sets as XML files, one for testing and another for production :



















When running program executable for the both configuration files (path to folder in which the actual configuration XML file exists, will be given as a parameter for executable), we get the following console printouts :










When the program starts, path to configuration folder is captured (command line argument). Calling any class property of static Configurations class will automatically trigger static class constructor. Static class constructor will then use XML serializer (ObjectXMLHandler<T> class) for creating Configuration object instance (nested class inside static Configurations class). Finally, properties of newly created Configuration object will be assigned to properties of static Configurations class.

For testing this scheme, just replicate XML configuration files, create a new console application (GlobalConfigurations) and copyPaste the following code into CS-file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;

namespace GlobalConfigurations
{
    public class Program
    {
        public static string configurationFilePathName;
        //
        static void Main(string[] args)
        {
            configurationFilePathName = args[0];
            //
            // print configurations
            ConfigurationTester configurationTester = new ConfigurationTester();
            configurationTester.Print();
        }
    }
    //
    //
    //
    /// <summary>
    /// Class for testing global static read-only configurations 
    /// </summary>
    public class ConfigurationTester
    {
        public void Print()
        {
            // print configurations
            Console.WriteLine("{0}, {1}, {2}, {3}", 
                Configurations.ConfigurationString, 
                Configurations.ConfigurationBoolean, 
                Configurations.ConfigurationLong, 
                Configurations.ConfigurationDouble);
        }
    }
    //
    //
    //
    /// <summary>
    /// Static class for hosting global configurations 
    /// </summary>
    public static class Configurations
    {
        // static read-only properties
        public static readonly string ConfigurationString;
        public static readonly bool ConfigurationBoolean;
        public static readonly long ConfigurationLong;
        public static readonly double ConfigurationDouble;
        //
        /// <summary>
        /// Static constructor will be called immediately 
        /// when static class property will be called. 
        /// </summary>
        static Configurations()
        {
            // create instance of ObjectXMLHandler for Configuration type
            ObjectXMLHandler<Configuration> handler = new ObjectXMLHandler<Configuration>();
            //
            // create Configuration objects from XML files
            string filePathName = Program.configurationFilePathName;
            List<Configuration> configuration = handler.Deserialize(filePathName);
            //
            // assign properties from inner configuration class into static read-only properties
            ConfigurationString = configuration[0].configurationString;
            ConfigurationBoolean = configuration[0].configurationBoolean;
            ConfigurationLong = configuration[0].configurationLong;
            ConfigurationDouble = configuration[0].configurationDouble;
        }
        /// <summary>
        /// Nested class for inner configurations. 
        /// </summary>
        public class Configuration
        {
            public string configurationString;
            public bool configurationBoolean;
            public long configurationLong;
            public double configurationDouble;
            //
            /// <summary>
            /// Default constructor is needed to create configuration parameters 
            /// </summary>
            public Configuration()
            {
                // no implementation
            }
        }
    }
    //
    //
    //
    /// <summary>
    /// Generic template class for handling conversion 
    /// between object and XML presentation.
    /// </summary>
    public class ObjectXMLHandler<T>
    {
        private XmlSerializer serializer = null;
        private Stream stream = null;
        //
        /// <summary>
        /// Parameter constructor for creating an instance. 
        /// </summary>
        public ObjectXMLHandler()
        {
            serializer = new XmlSerializer(typeof(T));
        }
        /// <summary>
        /// Convert a list of objects of type T into XML files. 
        /// </summary>
        public void Serialize(List<T> objects, List<string> fileNames, string folderPathName)
        {
            if (objects.Count != fileNames.Count)
                throw new Exception("objects.Count != fileNames.Count");
            //
            int counter = 0;
            foreach (T t in objects)
            {
                stream = File.Create(Path.Combine(folderPathName, String.Concat(fileNames[counter])));
                serializer.Serialize(stream, t);
                stream.Close();
                counter++;
            }
        }
        /// <summary>
        /// Convert XML files in specific folder into a list of objects of type T.
        /// </summary>
        public List<T> Deserialize(string folderPathName)
        {
            List<T> objects = new List<T>();
            foreach (string t in Directory.GetFiles(folderPathName))
            {
                stream = File.OpenRead(t);
                objects.Add((T)serializer.Deserialize(stream));
                stream.Close();
            }
            return objects;
        }
    }
}

Thanks a lot for using your precious time and reading my blog.
-Mike

No comments:

Post a Comment