Click here to Skip to main content
15,918,243 members
Articles / Programming Languages / C#

An Extension for a Configuration Settings Class in .NET

Rate me:
Please Sign up or sign in to vote.
4.55/5 (41 votes)
19 Aug 20031 min read 220.3K   1.3K   69   79
A class to allow easy access and updating to configuration settings for your .NET application

Introduction

In the System.Configuration namespace, you will find a sealed class called ConfigurationSettings which provides access to a static NameValueCollection entitled AppSettings. This ConfigurationSettings class allows you to access information stored in an XML-based configuration file. This file is typically titled the name of your executable followed by a ".config" file extension. The main inherent problem with this class is that your strictly only allowed to read values from configuration file through this class, no updating method is provided. I have addressed this issue below in the following ConfigSettings class. I have also updated the class to implement the IConfigurationSectionHandler interface which I plan on detailing within this article as soon as I have more time (Thanks Heath for the suggestion). Reflector was a big help along with simply reading the documentation. As always, if I left something out, please leave a message at the bottom. Hope this is of some help.

The following namespaces are required:

C#
using System;
using System.IO;
using System.Xml;
using System.Reflection;
using System.Collections;
using System.Globalization;
using System.Configuration;
using System.Collections.Specialized;

The class looks like the following:

C#
namespace Configuration
{
    public class ConfigSettingsSectionHandler : IConfigurationSectionHandler
    {
	static ConfigSettingsSectionHandler(){}

	public object Create(object parent, object configContext, 
                                                   System.Xml.XmlNode section)
	{
	    NameValueCollection col = new NameValueCollection(
	    new CaseInsensitiveHashCodeProvider(CultureInfo.InvariantCulture), 
		   new CaseInsensitiveComparer(CultureInfo.InvariantCulture));

		foreach(XmlNode node in section.ChildNodes)
		{
			switch(node.Name)
			{	
				case "add":
				col.Add(node.Attributes["key"].Value, 
					node.Attributes["value"].Value);
				break;
			}
		}
		return col;
	  }
     }	

public class ConfigSettings	
{
	private string _configfilename, _query, _sectionName;
	private XmlDocument _doc;
	private XmlTextWriter _writer;
	private XmlNodeList _nodes;
	private XmlElement  _appsettings, _node;
	private XmlAttribute _attr1, _attr2;
	private bool _bFileExists;

	public ConfigSettings()
	{
		if(File.Exists(Assembly.GetExecutingAssembly().ToString() + 
                                                               ".exe.config"))
		{
			this.ConfigFileName = 
                   Assembly.GetExecutingAssembly().ToString() + ".exe.config";
			_bFileExists = true;
		}
		else
		{
			_bFileExists = false;
		}
	}

	public ConfigSettings(string ConfigFileName)
	{
		if(File.Exists(ConfigFileName))
		{
			this.ConfigFileName = ConfigFileName;
			_bFileExists = true;
		}
		else
		{
			_bFileExists = false;
		}
	}
		
	public NameValueCollection GetConfig()
	{
		return (NameValueCollection)ConfigurationSettings.GetConfig(
                                                                 SectionName);
	}

	public NameValueCollection GetConfig(string SectionName)
	{
	    return (NameValueCollection)ConfigurationSettings.GetConfig(
                                                                 SectionName);
	}

	public string GetValue(string AttributeName)
	{
		NameValueCollection col = this.GetConfig();
		if(col[AttributeName] != null)
		       return Convert.ToString(col[AttributeName].ToString());
		else
			return String.Empty;
	}

	public void SetValue(string AttributeName, string Value)
	{
	        XmlDocument = new XmlDocument();
		XmlDocument.Load(this.ConfigFileName);
	        Query = "configuration/" + SectionName;
		AppSettingsNode = 
                    (XmlElement)this.XmlDocument.SelectSingleNode(this.Query);
		if(AppSettingsNode == null)
			return;
		Query += "/add[@key='" + AttributeName.ToString() + "']";
		XmlNodeList = this.XmlDocument.SelectNodes(this.Query);
		if(XmlNodeList.Count > 0)
			Node = (XmlElement)XmlNodeList[0];
		else
		{
			Node = this.XmlDocument.CreateElement("add");
			XmlAttribute1 = 
                                      this.XmlDocument.CreateAttribute("key");
			XmlAttribute1.Value = AttributeName.ToString();
			Node.Attributes.SetNamedItem(XmlAttribute1);
			XmlAttribute2 = 
                                    this.XmlDocument.CreateAttribute("value");
			Node.Attributes.SetNamedItem(XmlAttribute2);
			AppSettingsNode.AppendChild(Node);
		}
		Node.Attributes["value"].Value = Value.ToString();
		this.XmlDocument.Save(this.ConfigFileName);
	}

	public void CreateConfigFile(string ConfigFileName, 
                                                           string sectionName)
	{
	        FileStream file = new FileStream(ConfigFileName, 
                                                   System.IO.FileMode.Create);
		file.Close();
		Writer = new XmlTextWriter(ConfigFileName, 
                                                System.Text.Encoding.Unicode);
		Writer.Formatting = Formatting.Indented;
		Writer.WriteRaw("<?xml version=\"1.0\" ?>\n");
		Writer.WriteRaw("<configuration>\n");
		Writer.WriteRaw("<configSections>\n");
		string str = String.Format("<section type="{1}" name="{0}" />\n", 
                       "\"" + sectionName.ToString() + "\"",
             "\"Configuration.ConfigSettingsSectionHandler, Configuration\"");
		Writer.WriteRaw(str.ToString());
		Writer.WriteRaw("</configSections>\n");
		Writer.WriteRaw("<" + sectionName.ToString() + ">\n");
		Writer.WriteRaw("</" ? + sectionName.ToString()>\n");
		Writer.Flush();
		Writer.Close();
		}
	}

        public string ConfigFileName
	{
		get{ return _configfilename;}
		set{ _configfilename = value;}
	}
	public XmlDocument XmlDocument
	{
		get{ return _doc;}
		set{ _doc = value;}
	}
	public XmlTextWriter Writer
	{
		get{ return _writer;}
		set{ _writer = value;}
	}
	public XmlNodeList XmlNodeList
	{
		get{ return _nodes;}
		set{ _nodes = value;}
	}
	public XmlElement AppSettingsNode
	{
		get{ return _appsettings;}
		set{ _appsettings = value;}
	}
	public XmlElement Node
	{
		get{ return _node;}
		set{ _node = value;}
	}
	public string Query
	{
		get{ return _query;}
		set{ _query = value;}
	}
	public XmlAttribute XmlAttribute1
	{
		get{ return _attr1;}
		set{ _attr1 = value;}
	}
	public XmlAttribute XmlAttribute2
	{
		get{ return _attr2;}
		set{ _attr2 = value;}
	}
	public string SectionName
	{
		get{ return _sectionName;}
		set{ _sectionName = value;}
	}
	public bool FileExists
	{
		get{return _bFileExists;}
		set{_bFileExists = value;}
	}
}

Using the Class

To use the class, you will simply do one of the following:

C#
private ConfigSettings config = new ConfigSettings();

and then just use either the GetConfig(), GetValue(), SetValue() methods as such:

C#
  private void Form1_Load(object sender, System.EventArgs e)
  {
     config.ConfigFileName = "Test.exe.config";
     config.SectionName = "ApplicationData";
     string height = config.GetValue("Height");
     if(height != String.Empty)
         this.Height = Convert.ToInt32(height.ToString());

     // or return the whole collection of a particular section.

     NameValueCollection collection = config.GetConfig("ApplicationData");
     if(collection["Width"] != null)
          this.Width = Convert.ToInt32(collection["Width"].ToString());
}
C#
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
      config.SectionName = "ApplicationData";
      config.SetValue("Height", this.Height.ToString());
      config.SetValue("Width", this.Width.ToString());
}

Conclusion

I just thought I would post the latest updates, there are still many changes to come including a code clean-up as well as more methods to interact with the config file. Let me know what you think.

History

  • 20th August, 2003: Initial post

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
Software Developer (Senior)
United States United States
Nick graduated from Iowa State University with a B.S. in Management Information System and a minor in Computer Science. Nick works for Zetetic.

Nick has also been involved with the Iowa .NET User Group since it's inception, in particular giving presentations over various .NET topics. Nick was awarded the Visual C# MVP award from Microsoft for four years in a row.

In his mystical spare time he is working on a development project called "DeveloperNotes" which integrates into Visual Studio .NET allowing developers easy access to common code pieces. He is also a fan of using dynamically typed languages to perform unit testing, not to mention how he loves to talk about himself in the third person.

Comments and Discussions

 
GeneralConfiguration file Pin
Anonymous7-Oct-03 18:44
Anonymous7-Oct-03 18:44 
GeneralRe: Configuration file Pin
Nick Parker8-Oct-03 6:31
protectorNick Parker8-Oct-03 6:31 
GeneralCreating Pin
cosh31-Aug-03 8:24
cosh31-Aug-03 8:24 
GeneralRe: Creating Pin
Andres d7-Sep-03 2:58
Andres d7-Sep-03 2:58 
GeneralRe: Creating Pin
Nick Parker7-Sep-03 5:48
protectorNick Parker7-Sep-03 5:48 
GeneralIntegrated with the AAL! Pin
Marc Clifton16-Aug-03 15:59
mvaMarc Clifton16-Aug-03 15:59 
GeneralRe: Integrated with the AAL! Pin
Nick Parker16-Aug-03 17:36
protectorNick Parker16-Aug-03 17:36 
GeneralRe: Integrated with the AAL! Pin
Marc Clifton17-Aug-03 4:27
mvaMarc Clifton17-Aug-03 4:27 
Nick Parker wrote:
(and the book, any news on the forefront?)

Well, I've got the contract and I've written 3 chapters so far, and am struggling a bit with the fourth, which I thought would be easy to do--it's just about software development in general. But the typical chapters on the subject are always so boring--waterfall, top-down, bottom-up, etc., and I wanted to do something different, so that's what's making it challenging. The angle I'm working relates the history of hardware and language development to the challenges of software development, including the accelerating rate of technology change and availability).

A couple cool links I've found in my travels:

http://www.ox.compsoc.net/~swhite/history/timelines.html[^]

http://www.levenez.com/lang/[^]

My favorite gem so far that I've found is that Sun Microsystems originally developed Java as a means for unifying the Interactive TV development that flopped in the early 90's. Laugh | :laugh:

And I was rather blown away to read that Von Neumann, in 1945, envisioned software development to basically consist of stringing subroutines together--he had the concept of re-use 50 years ago!

And that's the cool thing about the class you wrote--it's incredibly easy to re-use. I looked at Microsoft's Configuration Management Application Block:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/daab-rm.asp[^]

and I was pretty dumbfounded--it's tightly integrated with Hashtables. So, in addition to what they developed, I would still have needed another layer for the GetValue/SetValue accessors. They should have provided these internally in the implementation!

And then there's this statement:

"Simplicity – a simple set of static (C#) / shared (Visual Basic .NET) methods allows you to read and write application configuration data without the need to instantiate objects or perform complex data conversions in your code"

which really made me laugh, because these classes return objects, or hashtables, which are collections of objects. So all the data conversion is left to the programmer! This is typical Microsoft--go only 1/2 way in the implementation.

And OMG, talk about overkill in the implementation. OK, there's some interesting things in there, and it's really general purpose, but come on, your class does the same thing in 1/100th of the lines of code. All that other baggage is unecessary, IMO, for most uses.

So as summary, you're class is USEFUL! It does one thing, and one thing well. It doesn't get entangled with container implementations, caching, config change event handlers, and different technology stores like SQL, registry, XML files, etc. What Microsoft did in this application block is exactly the kind of bad programming that I wrote about recently in the "what's wrong with objects" article--they've tightly coupled different technologies together, making it impossible to extract any one of them for a targeted solution. Blech. They got my "1" vote.

Marc

STL, a liability factory - Anonymously
A doable project is one that is small enough to be done quickly and big enough to be interesting - Ken Orr

Latest AAL Article
My blog

GeneralRe: Integrated with the AAL! Pin
Nick Parker17-Aug-03 6:16
protectorNick Parker17-Aug-03 6:16 
GeneralRe: Integrated with the AAL! Pin
Marc Clifton17-Aug-03 6:22
mvaMarc Clifton17-Aug-03 6:22 
GeneralRe: Integrated with the AAL! Pin
Nick Parker17-Aug-03 6:25
protectorNick Parker17-Aug-03 6:25 
GeneralRe: Integrated with the AAL! Pin
David Stone27-Aug-03 11:25
sitebuilderDavid Stone27-Aug-03 11:25 
GeneralRe: Integrated with the AAL! Pin
Nick Parker19-Aug-03 13:10
protectorNick Parker19-Aug-03 13:10 
GeneralCompiler erases config file - watch out Pin
nomondoan23-Jul-03 4:09
nomondoan23-Jul-03 4:09 
GeneralNot Meant to Change Pin
Chris Beckett22-Jul-03 5:38
Chris Beckett22-Jul-03 5:38 
GeneralRe: Not Meant to Change Pin
Nick Parker22-Jul-03 12:13
protectorNick Parker22-Jul-03 12:13 
GeneralRe: Not Meant to Change Pin
Chris Beckett22-Jul-03 13:41
Chris Beckett22-Jul-03 13:41 
GeneralRe: Not Meant to Change Pin
Nick Parker22-Jul-03 15:25
protectorNick Parker22-Jul-03 15:25 
GeneralRe: Not Meant to Change Pin
jamauss23-Jul-03 0:12
jamauss23-Jul-03 0:12 
GeneralRe: Not Meant to Change Pin
Nick Parker23-Jul-03 3:23
protectorNick Parker23-Jul-03 3:23 
GeneralRe: Not Meant to Change Pin
jamauss23-Jul-03 7:36
jamauss23-Jul-03 7:36 
GeneralRe: Not Meant to Change Pin
Pål K Tønder11-Aug-03 21:24
Pål K Tønder11-Aug-03 21:24 
GeneralRe: Not Meant to Change Pin
Alin19-Aug-03 6:38
Alin19-Aug-03 6:38 
GeneralRe: Not Meant to Change Pin
MtnBiknGuy28-Nov-03 9:27
MtnBiknGuy28-Nov-03 9:27 
GeneralIndexer Pin
reman21-Jul-03 21:10
reman21-Jul-03 21:10 

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.