Search This Blog

Tuesday, December 21, 2010

Custom Configuration Handler

I was going through a book about a different ways of reading configuration file and then I came across a scenario where it requires to get icon image url based on extensions of File. Of course there may be several ways of solving this issue but to me the most interesting one was to creating a section in the web.config for the extension's Image Url and a custom configuration section handler to read the same when used in an application. The code snippets are taken from the book but I made a solution to demonstrate its use in an actual scenario.


1) Lets say we plan to create the following mapping in xml to map a particular extension of file to Image Url.



               <unknownIcon imageUrl="Icons/unknown.gif" description="Unknown File Type"/>
<icon imageUrl="Icons/word.gif" description="Word Processing Document">
<ext>.DOC</ext>
<ext>.RTF</ext>
<ext>.TXT</ext>
</icon>
<icon imageUrl="Icons/excel.gif" description="Excel Document">
<ext>.XLS</ext>
<ext>.CSV</ext>
</icon>
<icon imageUrl="Icons/image.gif" description="Picture/Image">
<ext>.GIF</ext>
<ext>.TIFF</ext>
<ext>.JPG</ext>
<ext>.BMP</ext>
<ext>.PNG</ext>
</icon>
2) Let us create the Model class which will hold this data during run time, so I create a Class Library Project called "IconConfiguration" and name the newly added class as "IconConfigurationItem".



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace IconConfiguration
{
    public class IconConfigurationItem
    {
        private string _image;
        private string _description;
        private string _extension;


        public IconConfigurationItem(string image, string description, string extension)
        {
            this._image = image;
            this._description = description;
            this._extension = extension;
        }


        public string Image 
        { 
            get
            {
                return this._image;
            }
            set
            {
                this._image = value;
            }
        }
        public string Description 
        {
            get
            {
                return this._description;
            }
            set
            {
                this._description = value;
            }
        }
        public string Extension
        {
            get
            {
                return this._extension;
            }
            set
            {
                this._extension = value;
            }
        }
    }
}

3) The next step is to create a collection class which will hold each of these IconConfigurationItem. So I added another class to my Class Library Project and named it "IconConfigurationCollection". 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace IconConfiguration
{
    public class IconConfigurationCollection : CollectionBase
    {
        public IconConfigurationItem unknownIconInfo;
        private int getExtensionIndex(string extension)
        {
            int index = 0;
            for (index = 0; index < this.List.Count; index++)
            {
                IconConfigurationItem item = (IconConfigurationItem)this.List[index];
                if (item.Extension.Equals(extension.ToUpper().Trim()))
                    return index;
            }
            return -1;
        }
        public IconConfigurationItem getIconInfo(string extension)
        {
            int index = getExtensionIndex(extension);
            if (index == -1)
                return unknownIconInfo;
            else
                return (IconConfigurationItem)(this.List[index]);
        }
        public void Add(IconConfigurationItem obj)
        {
            this.List.Add(obj);
        }
    }
}

4) The very next step will be to create the Configuration Handler which will read the particular XML from my Web.config. It must inherit from IConfigurationSectionHandler and when you do that the intelligence will also write the method Create which needs to be implemented. My Configuration Handler class name is "IconConfigurationHandler".

using System.Configuration;
using System.Xml;
//<unknownIcon imageUrl="Icons/unknown.gif" description="Unknown File Type"/>
//<icon imageUrl="Icons/word.gif" description="Word Processing Document">
//    <ext>.DOC</ext>
//    <ext>.RTF</ext>
//    <ext>.TXT</ext>
//</icon>
//<icon imageUrl="Icons/excel.gif" description="Excel Document">
//    <ext>.XLS</ext>
//    <ext>.CSV</ext>
//</icon>
//<icon imageUrl="Icons/image.gif" description="Picture/Image">
//    <ext>.GIF</ext>
//    <ext>.TIFF</ext>
//    <ext>.JPG</ext>
//    <ext>.BMP</ext>
//    <ext>.PNG</ext>
//</icon>
namespace IconConfiguration
{
    class IconConfigurationHandler : IConfigurationSectionHandler
    {
        #region IConfigurationSectionHandler Members

        public object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
            IconConfigurationCollection returnObject = new IconConfigurationCollection();
            IconConfigurationItem configItem;

            XmlNodeList iconNodelist = section.SelectNodes("icon");
            XmlNodeList extensionNodeList;
            //XmlNode iconNode;
            //XmlNode extensionNode;

            foreach(XmlNode iconNode in iconNodelist) 
            {
                extensionNodeList = iconNode.SelectNodes("ext");
                foreach (XmlNode extensionNode in extensionNodeList)
                {
                    configItem = new IconConfigurationItem(
                        iconNode.Attributes.GetNamedItem("imageUrl").Value,
                        iconNode.Attributes.GetNamedItem("description").Value,
                        extensionNode.InnerText);
                    returnObject.Add(configItem);
                }
            }
            //Acquire and Process the Unknown Icon Node
            XmlNode unknownIconNode = section.SelectSingleNode("unknownIcon");
            if (unknownIconNode != null)
            {
                returnObject.unknownIconInfo = new IconConfigurationItem(
                    unknownIconNode.Attributes.GetNamedItem("imageUrl").Value,
                    unknownIconNode.Attributes.GetNamedItem("description").Value,
                  string.Empty);
            }
            return returnObject;
        }

        #endregion
    }
}

All it does is select icon and unknownIcon nodes from the custom section that we will add in the web.config and makes an object of type IconConfigurationItem and adds it to the Collection IconConfugurationCollection and returns the collection object.

5) We are almost done with the Library class, only the last thing which the Config.cs file/class which will read the web.config from the web project. But before writing this part, we can actually add a website to our solution and also add the custom section in the web.config, so we have a better idea of which section we will read before writing the config.cs file.

So I crated a Website and added a Page called "IconConfigHandler.aspx" and in the web.config, I added a section as

<!--Section for Custom Configuration Handler to pick up-->
<section name="IconConfig" type="IconConfiguration.IconConfigurationHandler, IconConfiguration"/> 

Note : The <configSections> element must be the first item defined in the <configuration> section. If you place any other elements in the <configuration> section before the <configSections> element, you will receive a compilation error.

So, This must be under configSections tag and the type is having a syntax like 
type="Namespace.Type, assembly". Now that we have added the new section name so we can added our mapping xml as in point (1) under the new section name i.e. "iconConfig", so it will read like the following

       <IconConfig>
<unknownIcon imageUrl="Icons/unknown.gif" description="Unknown File Type"/>
<icon imageUrl="Icons/word.gif" description="Word Processing Document">
<ext>.DOC</ext>
<ext>.RTF</ext>
<ext>.TXT</ext>
</icon>
<icon imageUrl="Icons/excel.gif" description="Excel Document">
<ext>.XLS</ext>
<ext>.CSV</ext>
</icon>
<icon imageUrl="Icons/image.gif" description="Picture/Image">
<ext>.GIF</ext>
<ext>.TIFF</ext>
<ext>.JPG</ext>
<ext>.BMP</ext>
<ext>.PNG</ext>
</icon>
</IconConfig>

This must be placed outside the <configSections> tag.

6) So we now write the Config class to read the above config

using System;
using System.Web;
using System.Collections.Generic;
using System.Configuration;

namespace IconConfiguration
{
    public static class Config
    {
        private const string sectionName = "IconConfig";
        private const string normalText = "normalRead";
        private static IconConfigurationCollection _iconData;
        public static IconConfigurationCollection IconData 
        { 
            get
            {
                if (_iconData == null)
                {
                    _iconData = (IconConfigurationCollection)ConfigurationManager.GetSection(sectionName);
                }
                return _iconData;
            }
        }
        public static string AppConfigData
        {
            get
            {
                return ConfigurationManager.AppSettings[normalText].ToString();
            }
        }
    }

}

*I have added a property called "AppConfigData" just to make sure that if I want to read without the custom section handler things are ok or not and that we will see shortly.

7) Next in the Webpage of the website I added a Textbox for entering the extension and a button, on click of which it will get the appropriate Image Url and also 2 lables, one to display the fetched url of the extension and the other one to read from App Settings without using the custom section handler. The code behind reads something like this as follows

protected void btnShowIcon_Click(object sender, EventArgs e)
    {
        string extension = txtEntension.Text.Trim();

        lblResult.Text = ((IconConfigurationItem)Config.IconData.getIconInfo(extension)).Image;
        lblRes.Text = Config.AppConfigData;
    }

Oh Boy ! It works just as expected.