Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C#
Article

Handle multiple configuration files

Rate me:
Please Sign up or sign in to vote.
4.28/5 (20 votes)
7 Dec 20032 min read 146.7K   655   62   11
Read and write any configuration file with ease

Sample Image - AnyConfig.jpg

Introduction

Here is another recipe for cooking up and serving your own configuration files. And I mean any configuration file, not just your ready-to-serve app.config and web.config files. Readers who need a refresher on the configuration file layout or mechanics can see prior articles published here or other references.

Background

I’ve never enjoyed being served an appetizer with the entrée and desert at the same time or in the same dish - it just doesn’t taste right. Besides, one of the main imperial rules of programming says: first divide, then conquer. So our goal here will be to break up the monolithic app.config and web.config files and gain some freedom from their confines and rules, without loosing any of the conveniences they provide. In other words, separate the data that we would normally place in one configuration file into several config files, so we can use them where needed and as needed. In doing so, the applications we develop may stand a better chance of getting past the security guards at the production environment gates. We often hear people say, "Don’t place all your eggs in the same basket" - same idea here.

The Code

Hungry? Let’s turn to the recipe. In order to remain compatible with the methods of getting to the key-value pairs provided in our default configuration files, and because of the convenience they provide, we choose to simply inherit them from the System.Configuration in our new class. Then we proceed to define some of our own methods.

C#
using System;
using System.Xml;
using System.Reflection;
using System.Configuration;
using System.Windows.Forms;

public class AnyConfig : System.Configuration.AppSettingsReader
{
    private XmlNode node;
    private string _cfgFile;

    public   string cfgFile
    {
        get    { return _cfgFile; }
        set    { _cfgFile=value; }
    }

    public string GetValue (string key)
    {
        return Convert.ToString(GetValue(key, typeof(string)));
    }

    public new object GetValue (string key, System.Type sType)
    {
        XmlDocument doc = new XmlDocument();
        object ro = String.Empty;
        loadDoc(doc);
        string sNode = key.Substring(0, key.LastIndexOf("//"));
        // retrieve the selected node
        try
        {
            node =  doc.SelectSingleNode(sNode);
            if( node != null )
            {
                // Xpath selects element that contains the key
                XmlElement targetElem= (XmlElement)node.SelectSingleNode(
                   key.Replace(sNode,"")) ;
                if (targetElem!=null)
                {
                    ro = targetElem.GetAttribute("value");
                }
            }
            if (sType == typeof(string))
                return Convert.ToString(ro);
            else
            if (sType == typeof(bool))
            {
                if (ro.Equals("True") || ro.Equals("False"))
                    return Convert.ToBoolean(ro);
                else
                    return false;
            }
            else
                if (sType == typeof(int))
                return Convert.ToInt32(ro);
            else
                if (sType == typeof(double))
                return Convert.ToDouble(ro);
            else
                if (sType == typeof(DateTime))
                return Convert.ToDateTime(ro);
            else
                return Convert.ToString(ro);
        }
        catch
        {
            return String.Empty;
        }
    }

    public bool SetValue (string key, string val)
    {
        XmlDocument doc = new XmlDocument();
        loadDoc(doc);
        try
        {
            // retrieve the target node
            string sNode = key.Substring(0, key.LastIndexOf("//"));
            node =  doc.SelectSingleNode(sNode);
            if( node == null )
                return false;
            // Set element that contains the key
            XmlElement targetElem= (XmlElement) node.SelectSingleNode(
                 key.Replace(sNode,""));
            if (targetElem!=null)
            {
                // set new value
                targetElem.SetAttribute("value", val);
            }
            // create new element with key/value pair and add it
            else
            {
                // handle xxx[@key='yyy']
                sNode = key.Substring(key.LastIndexOf("//")+2);
                // create new element xxx
                XmlElement entry = doc.CreateElement(sNode.Substring(0, 
                   sNode.IndexOf("[@")).Trim());
                sNode =  sNode.Substring(sNode.IndexOf("'")+1);
                // set attribute key=yyy
                entry.SetAttribute("key", sNode.Substring(0, 
                     sNode.IndexOf("'")) );
                // set attribute value=val
                entry.SetAttribute("value", val);
                node.AppendChild(entry);
            }
            saveDoc(doc, this._cfgFile);
            return true;
        }
        catch
        {
            return false;
        }
    }

    public bool removeElement (string key)
    {
        XmlDocument doc = new XmlDocument();
        loadDoc(doc);
        try
        {
            string sNode = key.Substring(0, key.LastIndexOf("//"));
            // retrieve the appSettings node
            node =  doc.SelectSingleNode("//appSettings");
            if( node == null )
                return false;
            // XPath select setting element that contains the key to remove
            node.RemoveChild( node.SelectSingleNode(key.Replace(sNode,"")) );
            saveDoc(doc, this._cfgFile);
            return true;
        }
        catch
        {
            return false;
        }
    }

    private void saveDoc (XmlDocument doc, string docPath)
    {
        // save document
        // choose to ignore if web.config since it may 
        // cause server sessions interruptions
        if(  this._cfgFile.Equals("web.config") )
            return;
        else
        try
        {
            XmlTextWriter writer = new XmlTextWriter( docPath , null );
            writer.Formatting = Formatting.Indented;
            doc.WriteTo( writer );
            writer.Flush();
            writer.Close();
            return;
        }
        catch
        {}
    }

     private void loadDoc ( XmlDocument doc )
    {
        // check for type of config file being requested
        if(  this._cfgFile.Equals("app.config"))
        {
            // use default app.config
            this._cfgFile = ((Assembly.GetEntryAssembly()).GetName()).Name+
               ".exe.config";
        }
        else
        if(  this._cfgFile.Equals("web.config"))
        {
            // use server web.config
            this._cfgFile = System.Web.HttpContext.Current.Server.MapPath(
              "web.config");
        }
        // load the document
        doc.Load( this._cfgFile );
    }

}

Below is a FormConfig class with a main method that just serves what we’ve cooked up thus far in a Windows form:

public class FormConfig : System.Windows.Forms.Form
{

    private System.Windows.Forms.TextBox textBox1;
    private System.Windows.Forms.DataGrid dataGrid1;
    private System.Windows.Forms.Button button1;
    private AnyConfig config;
    private DataSet ds;
    .
    .
    .
    static void Main()
    {
        Application.Run(new FormConfig());
    }

    private void FormConfig_Load(object sender, System.EventArgs e)
    {
        config = new AnyConfig();
        // set the document path\name
        config.cfgFile = "app.config";

        // retrieve and output document values
        textBox1.Text = "Sample output from "+ config.cfgFile+":\r\n";
        textBox1.Text+= config.GetValue("//appSettings//add[@key='one']");

        // retrieve and output another document values
        config.cfgFile = "..\\..\\my.config";
        textBox1.Text += "\r\n\r\nNow output from "+ 
            config.cfgFile+" and DataGrid:\r\n";
        DateTime date =    (DateTime)( config.GetValue(
          "//allSettings//mySettings//set[@key='date']", 
          typeof( DateTime )) );
        date = date.AddMonths( 1 );
        textBox1.Text+= date.ToShortDateString()+"\r\n";
        textBox1.Text+= config.GetValue(
           "//allSettings//mySettings//set[@key='greeting']")+
                " "+( config.GetValue(
            "//allSettings//mySettings//set[@key='salutation']") )+"\r\n";
        textBox1.Text+= (bool)( config.GetValue( 
            "//allSettings//mySettings//set[@key='switch']",
            typeof( bool )) ) +"\r\n";

        // remove a key/value pair
        config.removeElement("//allSettings//mySettings//set[@key='makes']");
        // add it back
        config.SetValue("//allSettings//mySettings//set[@key='makes']", 
                  "more sense" );

        // retrieve and output values from another set of keys
        textBox1.Text+= config.GetValue(
              "//allSettings//yourSettings//your[@key='greeting']")+
            " "+( config.GetValue(
         "//allSettings//yourSettings//your[@key='salutation']") )+"\r\n";

        // display document
        ds = new DataSet();
        ds.ReadXml(config.cfgFile);
//        dataGrid1.SetDataBinding(ds, "appSettings");
        dataGrid1.DataSource = ds.Tables["allSettings"];
        dataGrid1.Expand(-1);
    }
}

The output from the resulting application is the Windows form, shown here with mySettings expanded in the DataGrid:

Sample ouput

These are the app.config and my.config files we used as input:

XML
<?xml version="1.0" encoding="utf-8"?> <!-- app.config -->
<configuration>
 <appSettings>
    <add key="one" value="hello" />
    <add key="testing" value="1234506" />
    <add key="howdy" value="there" />
  </appSettings>
</configuration>

<?xml version="1.0" encoding="utf-8"?> <!-- my.config -->
<configuration>
  <allSettings>
    <mySettings>
      <set key="greeting" value="Hello" />
      <set key="date" value="4/9/1945" />
      <set key="test" value="makes sense" />
      <set key="salutation" value="Mr." />
      <set key="switch" value="True" />
      <set key="makes" value="sense" />
    </mySettings>
    <yourSettings>
      <your key="switch" value="False" />
      <your key="greeting" value="Ciao" />
      <your key="date" value="11/20/2003" />
      <your key="test" value="987654" />
      <your key="salutation" value="Ms." />
    </yourSettings>
  </allSettings>
</configuration>

Points of Interest

The FormConfig class handles two separate configuration files and shows how to update key values as well as delete and add key-value pairs. The AnyConfig.saveDoc method ignores saving any web.config updates, since it may cause undesirable server sessions interruptions. The example is just that - an example. One could use it to create their own .config files and refine any of the methods to support other types of values.

One Snoopy likes surfing and snooping, the other sniffing and cooking. Enjoy cooking!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect
United States United States
From the days of punch cards and Assembler code to kayaking on the Web - still having fun.
http://www.kayakonline.com/

Comments and Discussions

 
GeneralMy vote of 1 Pin
Member 1157962514-May-15 21:18
Member 1157962514-May-15 21:18 
GeneralGood job Pin
Redgum25-Jun-07 10:45
professionalRedgum25-Jun-07 10:45 
GeneralCrashes with a file dialog Pin
Kesler14-Dec-05 8:03
Kesler14-Dec-05 8:03 
GeneralPackaging and Deploy Pin
christian_shazam25-Aug-04 4:33
christian_shazam25-Aug-04 4:33 
GeneralVB.NET Version Pin
Anonymous12-May-04 0:42
Anonymous12-May-04 0:42 
Imports System
Imports System.Xml
Imports System.Reflection
Imports System.Configuration

Public Class AnyConfig
Inherits System.Configuration.AppSettingsReader

Private node As XmlNode
Private _cfgFile As String

Public Property cfgFile() As String
Get
Return _cfgFile
End Get
Set(ByVal Value As String)
_cfgFile = Value
End Set
End Property

Public Overloads Function GetValue(ByVal key As String) As String
Return Convert.ToString(GetValue(key, GetType(String)))
End Function

Public Overloads Function GetValue(ByVal key As String, ByVal sType As System.Type) As Object
Dim doc As New XmlDocument
Dim ro As Object = String.Empty
loadDoc(doc)

Dim sNode As String = key.Substring(0, key.LastIndexOf("//"))

Try
node = doc.SelectSingleNode(sNode)
If (Not node Is Nothing) Then
' Xpath selects element that contains the key
Dim targetElem As XmlElement = CType(node.SelectSingleNode(key.Replace(sNode, "")), XmlElement)
If (Not targetElem Is Nothing) Then
ro = targetElem.GetAttribute("value")
End If
End If

If (sType Is GetType(String)) Then
Return Convert.ToString(ro)
Else
If (sType Is GetType(Boolean)) Then
If (ro.Equals("True")) Or (ro.Equals("False")) Then
Return Convert.ToBoolean(ro)
Else
Return False
End If
ElseIf (sType Is GetType(Integer)) Then
Return Convert.ToInt32(ro)
ElseIf (sType Is GetType(Double)) Then
Return Convert.ToDouble(ro)
ElseIf (sType Is GetType(DateTime)) Then
Return Convert.ToDateTime(ro)
Else
Return Convert.ToString(ro)
End If
End If
Catch
Return String.Empty
End Try
End Function

Public Function SetValue(ByVal key As String, ByVal val As String) As Boolean
Dim doc As New XmlDocument
loadDoc(doc)
Try

' retrieve the target node
Dim sNode As String = key.Substring(0, key.LastIndexOf("//"))
node = doc.SelectSingleNode(sNode)
If (node Is Nothing) Then
Return False
End If

' Set element that contains the key
Dim targetElem As XmlElement = CType(node.SelectSingleNode(key.Replace(sNode, "")), XmlElement)
If (Not targetElem Is Nothing) Then

' set new value
targetElem.SetAttribute("value", val)

' create new element with key/value pair and add it
Else

' handle xxx[@key='yyy']
sNode = key.Substring(key.LastIndexOf("//") + 2)
' create new element xxx
Dim entry As XmlElement = doc.CreateElement(sNode.Substring(0, sNode.IndexOf("[@")).Trim())
sNode = sNode.Substring(sNode.IndexOf("'") + 1)
' set attribute key=yyy
entry.SetAttribute("key", sNode.Substring(0, sNode.IndexOf("'")) )
' set attribute value=val
entry.SetAttribute("value", val)
node.AppendChild(entry)
End If

saveDoc(doc, Me._cfgFile)
Return True
Catch
Return False
End Try
End Function

Public Function removeElement(ByVal key As String) As Boolean
Dim doc As XmlDocument = New XmlDocument
loadDoc(doc)
Try
Dim sNode As String = key.Substring(0, key.LastIndexOf("//"))
' retrieve the appSettings node
node = doc.SelectSingleNode("//appSettings")
If (node Is Nothing) Then
Return False
End If

' XPath select setting element that contains the key to remove
node.RemoveChild(node.SelectSingleNode(key.Replace(sNode, "")))
saveDoc(doc, Me._cfgFile)
Return True
catch
Return False
End Try
End Function

Private Sub saveDoc(ByVal doc As XmlDocument, ByVal docPath As String)
' save document
' choose to ignore if web.config since it may
' cause server sessions interruptions
If (Me._cfgFile.Equals("web.config")) Then
Return
Else
Try
Dim writer As XmlTextWriter = New XmlTextWriter(docPath, Nothing)
writer.Formatting = Formatting.Indented
doc.WriteTo(writer)
writer.Flush()
writer.Close()
Return

Catch
End Try
end if
End Sub

Private Sub loadDoc(ByVal doc As XmlDocument)
' check for type of config file being requested
'If (Me._cfgFile.Equals("app.config")) Then
' use default app.config
'Me._cfgFile = ((Assembly.GetEntryAssembly()).GetName()).Name + ".exe.config"
'ElseIf (Me._cfgFile.Equals("web.config")) Then
' use server web.config
'Me._cfgFile = System.Web.HttpContext.Current.Server.MapPath("web.config")
'End If

' load the document
doc.Load(Me._cfgFile)
End Sub

End Class

GeneralAnother Approach Pin
Steven Campbell14-Jan-04 9:10
Steven Campbell14-Jan-04 9:10 
GeneralremoveElement Returns false Pin
teeinep6-Jan-04 5:22
teeinep6-Jan-04 5:22 
GeneralRe: removeElement Returns false Pin
Paperless9-Jan-04 8:38
Paperless9-Jan-04 8:38 
GeneralCrashes after making names consistent Pin
Bryan White17-Dec-03 14:52
Bryan White17-Dec-03 14:52 
GeneralRe: Crashes after making names consistent Pin
WebSpiderMan17-Dec-03 17:08
WebSpiderMan17-Dec-03 17:08 
GeneralRe: Crashes after making names consistent Pin
Paperless19-Dec-03 11:30
Paperless19-Dec-03 11:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.