Geeks With Blogs
Willem's... {rue if I mellow}

The .NET 2.0 System.Configuration.dll provides a much more sophisticated (and complex) set of classes that access and modify application .config files, than compared to the functionality in .NET 1.x.

However, most often, the requirement is still to provide simple access to and from the key-value pairs in the appSettings section as before.

This post establishes a very simple abstract base-class that provides a simple binding to the appSettings section that requires no code to be written in the sub-class. It provides a Load method that is called in the  instance constructor. The Load method uses reflection to determine the public properties of the derived class. For each of these properties, the key-value instance in the appSettings is accessed, the value is read and converted from a string to the required property type, and the property value updated. There are two restrictions to this action:

  • The correct value type must be able to be instanced by the System.Convert.ChangeType method and converted from a System.String, so the type should be a simple (primitive) type such as a string or int, etc.
  • The property should be a simple value type which is not an array. The Load method checks for this using the System.Type.IsArray property, and ignores the property if the value is true.

Of course, it can be argued that if the application setting value does not adhere to those restrictions, it should not be stored in the appSettings section in the first place.

In the instance destructor, the Save method is called. Again using reflection, for each public property which is not an array, the key-value of the appSettings section is accessed and updated with the current value of the property. Note that the value is changed to a System.String, which is in keeping with the restrictions mentioned above. Once the settings are updated, they are saved back to the .config file and then reloaded, to ensure correct updating subsequently.

Note that the Load and Save methods are public so they can be called from external code at any time. They are also virtual so that they can be overridden by the derived class if required.

The C# code for the base-class is shown in Listing 1 below:

 

Listing 1: Application Settings Base Class
public abstract class AppSettings
{
  // Load the settings.
  public virtual void Load()
  {
    PropertyInfo[] properties = GetType().GetProperties();
    foreach (PropertyInfo property in properties)
    {
      if (!property.PropertyType.IsArray)
      {
        string appValue = ConfigurationManager.AppSettings[property.Name];
        if (appValue != null)
        {
          try
          {
            // Attempt to change the type from System.String to
            // the property type, and set the property value.
            property.SetValue(
              this,
              Convert.ChangeType(appValue, property.PropertyType),
              null);
          }
          catch {/*ignore*/}
        }
      }
    }
  }
  // Save the settings.
  public virtual void Save()
  {
    // Get the configuration file.
    System.Configuration.Configuration configuration =
      ConfigurationManager.OpenExeConfiguration(
        ConfigurationUserLevel.None);
    // Get the public properties of the class.
    PropertyInfo[] properties = GetType().GetProperties();
    // Save each property setting.
    foreach (PropertyInfo property in properties)
    {
      // Save if not an array type.
      if (!property.PropertyType.IsArray)
      {
        // Remove the setting if it exists.
        if (configuration.AppSettings.Settings[property.Name] != null)
        {
          configuration.AppSettings.Settings.Remove(property.Name);
        }
        // Add the setting.
        configuration.AppSettings.Settings.Add(
          property.Name,
          property.GetValue(this, null).ToString());
      }
    }
    // Save the configuration settings.
    configuration.Save(ConfigurationSaveMode.Modified);
    // Force a reload of the whole section.
    ConfigurationManager.RefreshSection("appSettings");
  }
  protected AppSettings()
  {
    // Load the settings when class instance is constructed.
    Load();
  }
  ~AppSettings()
  {
    // Save settings when instance is destroyed.
    Save();
  }
}

So how do you use it?

In Listing 2 is a test settings class. Note that the only requirement to get it all the work is to add the class inheritance reference. Now if your application creates an instance of the derived settings class, the .config file settings are loaded, if available. By simply destructing the instance (by normally letting it run out of scope), the settings are automatically saved for the derived class public properties. This also keeps working, whether you add or remove public properties to the derived class.

 

Listing 2: A test sub-class which is instantiable in an application.      
public class Settings : AppSettings
{
  private double doubleValue;
  public double DoubleValue
  {
    get { return doubleValue; }
    set { doubleValue = value; }
  }
  private decimal decimalValue;
  public decimal DecimalValue
  {
    get { return decimalValue; }
    set { decimalValue = value; }
  }
  private bool boolValue;
  public bool BoolValue
  {
    get { return boolValue; }
    set { boolValue = value; }
  }
  private int intValue;
  public int IntValue
  {
    get { return intValue; }
    set { intValue = value; }
  }
  private string stringValue;
  public string StringValue
  {
    get { return stringValue; }
    set { stringValue = value; }
  }
  private DateTime dateTimeValue;
  public DateTime DateTimeValue
  {
    get { return dateTimeValue; }
    set { dateTimeValue = value; }
  }
  public Settings() { }
}

Here is what the .config file looks like for this example:

 

Listing 3: The .config file showing the settings stored as keys.          
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <add key="DoubleValue" value="3.141592654" />
        <add key="DecimalValue" value="-100.01" />
        <add key="BoolValue" value="True" />
        <add key="IntValue" value="1024" />
        <add key="StringValue" value="The quick brown fox jumped over
           the lazy dog." />
        <add key="DateTimeValue" value="2006/11/13 12:34:56 AM" />
    </appSettings>
</configuration>

You can download the AppSettings class and a sample application from the link below:

Posted on Monday, November 13, 2006 11:26 AM .NET Adventures | Back to top


Comments on this post: An ultra-simple appSettings class for .NET 2.0+

# re: An ultra-simple appSettings class for .NET 2.0+
Requesting Gravatar...
Very nice class.
Left by Fellow .Net Coder on Dec 14, 2006 4:36 AM

# re: An ultra-simple appSettings class for .NET 2.0+
Requesting Gravatar...
"StringValue" is not really saved ;-) Why?

Cheers,
Dawid Ireno - MCP
Left by alamandra on Jun 18, 2007 2:36 PM

# re: An ultra-simple appSettings class for .NET 2.0+
Requesting Gravatar...
OK, sth. really sucks about this code, but I don't know what so far. Strings are saved - my mistake. But constructors sometimes do not initialize instances at all. And sometimes they partially do. This class is really really bugged!

Cheers,
Dawid Ireno - MCP
Left by alamandra on Jun 18, 2007 4:03 PM

# re: An ultra-simple appSettings class for .NET 2.0+
Requesting Gravatar...
Dawid,

I have updated the post with some sample code that you can download. I hope this helps you to solve your problem.

One thing to remember: the class is to allow the easy management of primitive types - certainly reference types are not catered for.

If this does not help, maybe you can post-up some code or provide a more-detailed bug report?

Best regards, Willem
Left by Willem Fourie on Jun 19, 2007 2:42 AM

# re: An ultra-simple appSettings class for .NET 2.0+
Requesting Gravatar...
Hi Willem,

I've worked out some modifications to the solution. I renamed it to "UltraAppSettings", while it's not really compatible with appSetting section readers from .NET Framework - they are not notified about changes in appSetting section.
The line
ConfigurationManager.RefreshSection("appSettings");
does not do it's job. Why? I don't know. Some more work needs to be done.

I modified the "Load()" method, to make use of XmlDocument while collecting property values. Now the method always works fine.

I also made class implementing IDisposable, while otherwise you never know when object is destroyed and sometimes get an unexpected error that 'some other application modified config file'. This is when you started writing configuration values, finished it, and some time after it again tried to write some values. It is not possible than, when the first instance was not disposed. Every time you finished using an instance of UltraAppSettings you have to dispose it.

Cheers,
Dawid Ireno - MCP
Left by D.I. on Jul 03, 2007 4:59 PM

# re: An ultra-simple appSettings class for .NET 2.0+
Requesting Gravatar...
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Configuration;
using System.Xml;
using System.IO;

namespace System.Configuration
{
public abstract class UltraAppSettings : IDisposable
{
public string FilePath
{
get
{
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
return configuration.FilePath.Replace(".vshost.exe.config", ".exe.config");
}
}

public virtual void Load()
{
PropertyInfo[] properties = GetType().GetProperties();
XmlDocument d = new XmlDocument();
d.Load(this.FilePath);
XmlNode appSettings = d.DocumentElement.SelectSingleNode("appSettings");
foreach (PropertyInfo property in properties)
{
if (!property.PropertyType.IsArray)
{
string appValue = null;
try
{
XmlNode n = appSettings.SelectSingleNode("add[@key='" + property.Name + "']");
if (n != null)
{
appValue = n.Attributes["value"].InnerText;
}
}
catch (NullReferenceException)
{
}
if (appValue != null)
{
try
{
property.SetValue(this, Convert.ChangeType(appValue, property.PropertyType), null);
}
catch
{
}
}
}
}
}

public virtual void Save()
{
StringHelper h = new StringHelper(this.FilePath);
Configuration configuration = ConfigurationManager.OpenExeConfiguration(h.TrimEnd(".config"));
PropertyInfo[] properties = GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
if (!property.PropertyType.IsArray)
{
if (configuration.AppSettings.Settings[property.Name] != null)
{
configuration.AppSettings.Settings.Remove(property.Name);
}
if (property.GetValue(this, null) != null)
{
configuration.AppSettings.Settings.Add(property.Name, property.GetValue(this, null).ToString());
}
}
}
configuration.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
}

protected UltraAppSettings()
{
Load();
}

public void Dispose()
{
Save();
}
}
}
Left by D.I. on Jul 03, 2007 5:00 PM

# re: An ultra-simple appSettings class for .NET 2.0+
Requesting Gravatar...
StringHelper h = new StringHelper(this.FilePath);

Where can I see this class?
Left by Nikolay Sonin on Sep 22, 2008 2:58 PM

# re: An ultra-simple appSettings class for .NET 2.0+
Requesting Gravatar...
I dont understand
Left by Raji on Dec 11, 2009 2:42 PM

Your comment:
 (will show your gravatar)


Copyright © Willem Fourie | Powered by: GeeksWithBlogs.net