Click here to Skip to main content
15,881,092 members
Articles / Programming Languages / XML
Article

Need a SetConfig method for your Configuration Settings? What about an AppSettingsWriter?

Rate me:
Please Sign up or sign in to vote.
3.63/5 (14 votes)
20 Jan 20063 min read 50.1K   678   33   16
Here are the configuration classes that allow you to modify your config file.

Image 1

Introduction

I've been using the System.Configuration classes in all kinds of applications and services. I find them very useful, and until recently, it was all that I needed to work with the XML application settings.

Background

I've made a Visual Studio add-in. I intend to publish an article about that too. Actually, I got the idea on writing this article about configuration settings while I was working on the add-in. So, this add-in has a main form that you can click on in order to set some options and start the action. The first idea that came to my mind was to add the App.config file to store the predefined options and load them while starting. The next second, I realized that there’s no exe output from my solution so I cannot have an App.config file. Moreover, it would be nice if the options, when modified, could be persisted into the config file. Well, that’s the intrigue.

Explaining the code

There are two classes:

  • ConfigurationSettingsRW,
  • AppSettingsRW

and an interface:

  • IConfigurationRWSectionHandler

corresponding to the ones in the System.Configuration namespace.

'RW' stands for 'Read Write'.

IConfigurationRWSectionHandler interface

C#
public interface IConfigurationRWSectionHandler
{
    object Create(XmlNode section);
    void Persist(object config, XmlNode section);
}

The Create method implementation should take care of loading the data from the XmlNode into an instance of the desired type (e.g. a collection of some kind) that would be returned.

The Persist method implementation should consider the first parameter of the same type as the instance returned by the Create method and should modify the XmlNode parameter accordingly.

ConfigurationSettingsRW class

You have the AppSettings property and the GetConfig method of course, but they're not static. The config file path has to be passed as a string to the constructor when creating an instance of this class => no more naming conventions for the config file. There are two public methods dealing with the appSettings config section: LoadAppSettings and SaveAppSettings, with no parameters and no return (void). You have nothing in the AppSettings property until you call LoadAppSettings. Setting or adding a value of a specified key in appSettings is made by using the indexer of AppSettings with the string parameter and calling SaveAppSettings afterwards.

Besides the GetConfig public method, you have the corresponding SetConfig public method. Both are calling a private method named GetSectionData for getting the section XmlNode and the section handler interface. I did nothing about section groups... because I never used them. Feel free to extend the code in order to suit your needs.

C#
private void GetSectionData(string sectionName, 
      out XmlNode sectionNode, out IConfigurationRWSectionHandler handler)
{
    sectionNode = null;
    handler = null;
    try
    {
        XmlDocument configXml = new XmlDocument();
        configXml.Load(this.configFilePath);
        XmlNode node = 
          configXml.SelectSingleNode(string.Format("configuration" + 
          "/configSections/section[@name='{0}']", sectionName));
        if (node != null)
        {
            sectionNode = 
              configXml.SelectSingleNode(string.Format("configuration/{0}", 
                                                             sectionName));
            string typeName = node.Attributes["type"].Value;
            Type type = Type.GetType(typeName);
            handler = (IConfigurationRWSectionHandler)
                       Activator.CreateInstance(type);
        }
    }
    catch (Exception ex)
    {
        throw new ConfigurationException(string.Format("Error" + 
                  " while loading {0} section.", sectionName), ex);
    }
}

The SetConfig method saves the data by calling the specified (by 'configuration/configSections/section/@type' value) IConfigurationRWSectionHandler.Persist method. Of course, GetConfig gets the data by calling the corresponding IConfigurationRWSectionHandler.Create method.

AppSettingsRW class

The constructor has to get the config file path as a string parameter.

There is the AppSettings public property of type NameValueCollection.

There are the LoadFromFile and SaveToFile public methods that do exactly what their names suggest. Check this out:

C#
/// <summary>
/// Loads the <appSettings> config section data 
/// from the configuration file into the 
/// <code>appSettings</code> member of type 
/// <code>NameValueCollection</code>.
/// </summary>
public void LoadFromFile()
{
    if (appSettings == null)
    {
        try
        {
            XmlDocument configXml = new XmlDocument();
            configXml.Load(this.configFilePath);
            XmlNode appSettingsNode = 
                configXml.SelectSingleNode("configuration/appSettings");
            if(appSettingsNode.LocalName=="appSettings")
            {
                NameValueSectionHandler handler = 
                    new NameValueSectionHandler();

// The NameValueCollection instance returned by the 'Create' method is readonly.
// Assigning this instance to 'appSettings' member would not allow modifications.
// It seems that by passing this instance to the NameValueCollection constructor
// there is a similar instance created that is NOT readonly. Hmmm...
                appSettings = new NameValueCollection(
                    (NameValueCollection)handler.Create
                    (null, null, appSettingsNode));

            }
        }
        catch (Exception ex)
        {
            throw new ConfigurationException("Error while loading appSettings." +
                " Message: " + ex.Message, ex);
        }
    }
}

/// <summary>
/// Loops through the <code>appSettings</code> NameValueCollection 
/// and recreates the XML nodes of the <appSettings> config 
/// section accordingly. It saves the configuration file afterwards.
/// </summary>
public void SaveToFile()
{
    if (appSettings != null)
    {
        try
        {
            XmlDocument configXml = new XmlDocument();
            configXml.Load(this.configFilePath);
            XmlNode appSettingsNode = 
                configXml.SelectSingleNode("configuration/appSettings");
            appSettingsNode.RemoveAll();
            for (int i = 0; i < appSettings.Count; i++)
            {
                string key = appSettings.GetKey(i);
                string val = appSettings.Get(i);

                XmlNode node = configXml.CreateNode
                    (XmlNodeType.Element, "add", "");

                XmlAttribute attr = configXml.CreateAttribute("key");
                attr.Value = key;
                node.Attributes.Append(attr);

                attr = configXml.CreateAttribute("value");
                attr.Value = val;
                node.Attributes.Append(attr);

                appSettingsNode.AppendChild(node);
            }
            configXml.Save(this.configFilePath);
        }
        catch (Exception ex)
        {
            throw new ConfigurationException
                ("Error while saving appSettings.", ex);
        }
    }
}

And there are two other public methods: GetValue and SetValue. I'm sure you guessed what these are for. Check the source file for details.

Using the code

You can include the source file (ConfigurationSettingsRW.cs) in your project and use the classes in the same manner as you'd use the System.Configuration classes, with the few differences and additions described above. Check out the available sample solution and sources (ConfigurationSettingsRW_src.zip) to see how it's done. The config file's schema must be the one you know from the .NET Framework, with the 'configSections', 'appSettings', and everything else.

As I mentioned, I intend to write an article about some add-in. That would be a good usage sample, and I guess I could insert a link to it as soon as I get it done. Until then, you have a demo project available for download.

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
CEO http://startech.ro
Romania Romania
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionFileUpload control for mobile application Pin
Sheris18-Feb-09 3:20
Sheris18-Feb-09 3:20 
AnswerRe: FileUpload control for mobile application Pin
Alexandru Stanciu18-Feb-09 3:42
Alexandru Stanciu18-Feb-09 3:42 
GeneralRe: FileUpload control for mobile application Pin
Sheris23-Feb-09 16:12
Sheris23-Feb-09 16:12 
GeneralDude, most of us come to see how to use your code Pin
deerchao14-Aug-07 1:51
deerchao14-Aug-07 1:51 
GeneralRe: Dude, most of us come to see how to use your code Pin
Alexandru Stanciu14-Aug-07 2:18
Alexandru Stanciu14-Aug-07 2:18 
GeneralRe: Dude, most of us come to see how to use your code Pin
deerchao14-Aug-07 2:34
deerchao14-Aug-07 2:34 
GeneralAn alternative Pin
Drew Noakes23-Jan-06 23:37
Drew Noakes23-Jan-06 23:37 
GeneralRe: An alternative Pin
Edwin Roetman24-Jan-06 22:24
Edwin Roetman24-Jan-06 22:24 
GeneralNormally not allowed Pin
Ramon Smits22-Dec-05 22:40
Ramon Smits22-Dec-05 22:40 
AnswerRe: Normally not allowed Pin
Alexandru Stanciu22-Dec-05 22:56
Alexandru Stanciu22-Dec-05 22:56 
GeneralRe: Normally not allowed Pin
Danny Rodriguez23-Dec-05 3:13
Danny Rodriguez23-Dec-05 3:13 
AnswerRe: Normally not allowed Pin
Alexandru Stanciu23-Dec-05 5:16
Alexandru Stanciu23-Dec-05 5:16 
GeneralRe: Normally not allowed Pin
Gavin Roberts10-Jan-06 3:53
Gavin Roberts10-Jan-06 3:53 
GeneralRe: Normally not allowed Pin
Alexandru Stanciu10-Jan-06 4:39
Alexandru Stanciu10-Jan-06 4:39 
GeneralRe: Normally not allowed Pin
Gavin Roberts10-Jan-06 4:50
Gavin Roberts10-Jan-06 4:50 
GeneralRe: Normally not allowed Pin
John Whitmire11-Aug-06 9:54
professionalJohn Whitmire11-Aug-06 9:54 

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.