Search
Close this search box.

Read/Write App.config with .NET 2.0/Enterprise Library

This time I would like to show you the most important changes in the System.Configuration namespace with .NET 2.0.
I have looked at my blog referrer statistics and saw about 20 hits/day by google. Most of them were searching 
infos how to configure the new Enterprise Library but also a significant number of people which seem to seek guidance to the
following questions:

  • How to read/write to App.Config?
  • How to store a list of objects in a config file via the System.Configuration mechanism?

Reason enough for me to shed more light on the System.Configuration namespace.
The main changes from .NET 1.0/1.1 in the System.Configuration namespace are:

  • Write to your App.Config file through the Configuration class.
  • New configuration model for Windows Forms applications.
  • Store complex objects including object collections in your App.Config File.
  • It is possible to store Connection Strings in the App.Config file see ConnectionSettings this enables you to store you settings on a SQL Server. The Enterprise Library for Sample SqlConfiguration exercises this by implementing a SqlConfigurationSource which can store and retrieve a ConfigurationSection.

So where to start? I think I show you at first the config file and explain how you can create it programtically in your application.

The easiest way to read/write AppSettings

If you want to store only key/value pairs in your App.config file there is a special section in reserved which allows you to do exactly that. Simply add an <appsettings> section and add your data as key/value pairs of the form <add key=”xxx” value=”xxxx” />. Thats all to create new app.config file with settings in it.

App.Config<? xml version = "1.0" encoding = "utf-8"?>

    <configuration>

    <appSettings>

    <add key = "Setting1" value = "Very" />

    <add key = "Setting2" value = "Easy" />

    </ appSettings>

    </ configuration>

The data access API has been changed for this type of setting with .NET 2.0. The “old” one liner ConfigurationSettings.AppSettings  has been deprecated in favor of ConfigurationManager.AppSettings. Beside from the naming change you can now also write your application settings. For read only access you can look at the ShowConfig function defined below. Writing the last modification time is demonstrated in the Main function.

using System;

using System.Collections.Generic;

using System.Text;

using System.Configuration;

namespace AppSettings

{

  class Program

  {
    static void ShowConfig()

    {
      // For read access you do not need to call the OpenExeConfiguraton

      foreach (string key in ConfigurationManager.AppSettings)

      {
        string value = ConfigurationManager.AppSettings[key];

        Console.WriteLine("Key: {0}, Value: {1}", key, value);
      }
    }

    static void Main(string[] args)

    {
      ShowConfig();

      // Open App.Config of executable

      System.Configuration.Configuration config =

          ConfigurationManager.OpenExeConfiguration(
              ConfigurationUserLevel.None);

      // Add an Application Setting.

      config.AppSettings.Settings.Add("Modification Date",

                                      DateTime.Now.ToLongTimeString() + " ");

      // Save the configuration file.

      config.Save(ConfigurationSaveMode.Modified);

      // Force a reload of a changed section.

      ConfigurationManager.RefreshSection("appSettings");

      ShowConfig();
    }
  }

}

Expected Output:Key: Settings1, Value: Very
Key: Setting2, Value: Easy
Key: Settings1, Value: Very
Key: Setting2, Value: Easy
Key: Modification Date, Value: 01:21:03

With this mechanism you can read and update simple key/value pairs within your application without digging any deeper in the System.Configuration namespace. The following examples show the other features like the new windows forms configuration mechanism, create your own configuration section and how you can easily store lists of objects in the App.config file with the new Enterprise Library helper classes.

Windows Forms Configuration

When developing Windows Forms with VS2005 you get for free a new configuration mechansim. They Forms designers were so nice to create from your config values automatically an acessor class and came up with an consistant model to store application global config files in the app.config.exe file and user specific settings within the user profile in user.config.
Please note that the Forms configuration model is not availiable in class library projects. You can store your forms settings where ever you would like to. Any provider can be plugged into your config data accessor class by decorating your configuration class with the SettingsProviderAttribute. If none is specified the LocalFileSettingsProvider is used which relies on the System.Configuration. This is the reason why you do not need to reference the System.Configuration assembly in a windows forms but you see the System.Configuration assembly loaded in your windows forms application. You can check it with the debugger in the loaded modules list.
Below is a new Windows Forms project shown  which was generated via New->Project->Windows Application. The new configuration features are visible in the Properties folder of your project. There go your resources and the automatically generated strongly typed resource accessor class with static properties to allow easy and type safe access to your resources. This is similar to the old C programming model with windows resources. You had an header file with resource ids generated by the resource compiler which spits out an header file which is compiled (compile time checking of the existence of resources) and an object file which is linked into you target. Now you have also compile time checking in .NET if you access your resources via the static properties.
The configuration features surface in the auto generated Settings.settings file and Settings.Designer.cs.  To create new configuration values you have full Desinger integration within Visual Studio (see picture below). In your code you can read/modify these settings via the generated accessor class. Inside the visual editor you can choose between two scopes for each of your configuration settings: Application and User. The Application scope defines configuration values which cannot be changed by the user and are the same for all users of this application. User scoped settings on the other hand can be changed/created by, well the users and are stored within their local profile.

VS 2005 generated Windows Forms application skeleton.

Settings.settings (Generated by Visual Studio)

<?xml version=’1.0′ encoding=’utf-8′?>

                <SettingsFile xmlns=”http://schemas.microsoft.com/VisualStudio/2004/01/settings” CurrentProfile=”(Default)” GeneratedClassNamespace=”WindowsApplication1.Properties” GeneratedClassName=”Settings”>

                  <Profiles />

                  <Settings>

                    <Setting Name=”testSetting” Type=”System.String” Scope=”User”>

                      <Value Profile=”(Default)”>Form1</Value>

                    </Setting>

                  </Settings>

                </SettingsFile>

Settings.Designer.cs (Generated by Visual Studio using the SettingsSingleFileGenerator as Custom Build Tool)

namespace WindowsApplication1.Properties

{

  [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]

  [global::System.CodeDom.Compiler.GeneratedCodeAttribute(
      "Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator",
      "8.0.0.0")]

  internal sealed partial class Settings
      : global::System.Configuration.ApplicationSettingsBase

  {
    private static Settings defaultInstance =
        ((Settings)(global::System.Configuration.ApplicationSettingsBase
                        .Synchronized(new Settings())));

    public static Settings Default

    {
      get

      {
        return defaultInstance;
      }
    }

    [global::System.Configuration.UserScopedSettingAttribute()]

    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]

    [global::System.Configuration.DefaultSettingValueAttribute("Form1")]

    public string testSetting

    {
      get

      {
        return ((string)(this["testSetting"]));
      }

      set

      {
        this["testSetting"] = value;
      }
    }
  }

}

To load your settings programatically you only need to to  do a

Settings set = Settings.Default;

and access your settings via the property of the returned instance.

            string str = set.testSetting;

Wow that was easy. Wasn´t it? Now lets save our changed test setting:

            set.testSetting = “test value”;

That’s pretty much it. To display some of your settings in your form you can use data binding and let your users configure the application font, color, …. User specific settings are stored in  %APPDATA%\<AppName>\<AppName><AppConfigName_GUID>\<AssemblyVersion>\user.config. The path to the user config is on my machine is e.g. %APPDATA%\WindowsApplication1\WindowsApplication1.exe_Url_x00ebzylso3e0rtwd1pnxkhduwr34pgb\1.0.0.0. This enables you to install a Windows Forms App as Administrator for all users with some global settings in the executable App.config and user specific settings which are stored in the user profile. If your users are in a domain with a roaming profile they will get the same profile and thus the same user settings on every computer they work.

Another Way to read/write configuration values

The previous approach has its limits since you cannot be ensure that another third party component uses the same key to store its configuration values. To make your configuration values distinguishable from other configuration values we need to introduce our own configuration section inside the App.config file. It is a little more complicated since you have to write your own class which content is de/serialized to the App.config file. I am going to show you at first the config file and explain then what code you need to write to read/save these settings to your application configuration file.

App.Config(Taken from the Enterprise Library Configuration Migration QuickStart
Sample)

App.Config(Taken from the Enterprise Library Configuration Migration QuickStart
               Sample)<configuration>

    <configSections>

    <section name = "EditorSettings" type =
         "ConfigurationMigrationQuickStart.EditorFontData, ConfigurationMigrationQuickStart, Version=1.1.0.0, Culture=neutral, PublicKeyToken=null" />

    </ configSections>

    <EditorSettings name = "Verdana" size = "24" style = "2" />

    </ configuration>

Most App.config files which contain config data have a <configSections> element where many <section> are defined. The name attribute of an section (in this example “EditorSettings”) tells the config system that the class ConfigurationMigrationQuickStart.EditorFontData is responsible for the ser/deserialization of the node <EditorSettings>. The EditorFontData class derives from the ConfigurationSection class and uses the ConfigurationProperty attribute to create a mapping between the properties to de/serialize and the attribute names in names in the App.Config file.

using System.Text;

using System.Configuration;

public class EditorFontData : ConfigurationSection

{
public EditorFontData()

{}

[ConfigurationProperty(“name”)]

public string Name

{
get { return (string)this[“name”]; }

set { this["name"] = value; }

}

[ConfigurationProperty(“size”)]

public float Size

{
get { return (float)this[“size”]; }

set { this["size"] = value; }

}

[ConfigurationProperty(“style”)]

public int Style

{
get { return (int)this[“style”]; }

set { this["style"] = value; }

}
}

To access an EditorFontData instance with values from your config file you only need to call

EditorFontData configData = ConfigurationManager.GetSection(“EditorSettings”) as EditorFontData;Please note that the System.Configuration.ConfigurationManager returns only objects with read only properties. Subsequent calls to GetSection use the cached instance inside the ConfigurationManager. This constraint requires you to create every time a new instance of you e.g. EditorFontData object if you want to write to the App.config file. You even cannot add an object with the same name twice to the System.Confguration.Configuration object. Now comes the fun part: To edit an existing App.config file you have to remove your config object and then add a new object instance to the Configuration object. Only then the Save will succeed.

EditorFontData configData = new EditorFontData();

configData.Name = "Arial";

configData.Size = 20;

configData.Style = 2;

// Write the new configuration data to the XML file

Configuration config =
    ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

config.Sections.Remove("EditorSettings");

config.Sections.Add("EditorSettings", configData);

config.Save();

To get your hands on the System.Configuration.Configuration object you have to open your App.Config file. The .NET config mechanism supports setting inheritance from the Machine.config from which all settings are inherited. Next comes the App.Config file which is selected by the ConfigurationUserLevel.None file.

Enterprise Library Configuration Helpers

The Enterprise Library for .NET 2.0 has made it a lot easier to use the System.Configuration classes.More info how to configure the new application blocks you can  be found in another article. Lets suppose you want to store a collection of strongly typed named objects in our App.Config file. In this example it is a plugin configuration file where a list of plugins can be added and the selectd plugin be started. The config file could look like this:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>


  <configSections>

    <section name="PluginConfiguration" type="EntlibInjection.PluginManagerConfiguration, EntlibInjection, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" />

  </configSections>

 

  <PluginConfiguration name="Configured Plugins" SelectedPlugin="2D">

    <Plugins>

      <!-- type="EntlibInjection.PluginData, EntlibInjection, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" -->

      <add name="2D" cfgfile="2D.config" plugintype="EntlibInjection.TwoD, EntlibInjection, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" type=""/>

      <add name="3D" plugintype="ThreeDPlugin.ThreeD, ThreeDPlugin, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null" type=""/>

    </Plugins>

  </PluginConfiguration>

We want a PluginConfiguration class which selects a plugin and contains a list of plugins. To make it work we first need to define the PluginManagerConfiguration class. We need the Enterprise Library  only for the IConfigurationSource interface to load our config from other files than App.config or even an SQL server.

PluginManagerConfiguration.cs
using System;

using System.Configuration;

using System.ComponentModel;

using System.Collections.Generic;

using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

namespace EntlibInjection

{

  public class PluginManagerConfiguration : SerializableConfigurationSection

  {
    private const string selectedPlugin = "SelectedPlugin";

    private const string PluginCollectionProperty = "Plugins";

    private const string nameProperty = "name";

    /// <summary>

    /// Configuration section name for Plugin Configuration

    /// </summary>

    public const string SectionName = "PluginConfiguration";

    public PluginManagerConfiguration()

    {}

    /// <summary>

    /// Gets the configured plugin settings section in the configuration source.

    /// </summary>

    /// <param name="configurationSource">The <see cref="IConfigurationSource"/>
    /// to get the section from.</param>

    /// <returns>The plugin configuration.</returns>

    public static PluginManagerConfiguration GetPluginSettings(
        IConfigurationSource configurationSource)

    {
      return (PluginManagerConfiguration)configurationSource.GetSection(
          PluginManagerConfiguration.SectionName);
    }

    /// <summary>

    /// Get/Set the selected plugin

    /// </summary>

    [ConfigurationProperty(selectedPlugin, IsRequired = true)]

    public string SelectedPlugin

    {
      get

      {
        return (string)this[selectedPlugin];
      }

      set

      {
        this[selectedPlugin] = value;
      }
    }

    /// <summary>

    /// Name of the configuration node.

    /// </summary>

    [ConfigurationProperty(nameProperty)]

    [EditorBrowsable(EditorBrowsableState.Never)]

    public string Name

    {
      get

      {
        return (string)this[nameProperty];
      }

      set

      {
        this[nameProperty] = value;
      }
    }

    /// <summary>

    /// This property contains a list of plugins

    /// </summary>

    [ConfigurationProperty(PluginCollectionProperty)]

    public PluginDataCollection Plugins

    {
      get

      {
        return (PluginDataCollection)base[PluginCollectionProperty];
      }
    }
  }

}

</configuration>

After the definition of the Configuration Section we need the collection class which contains a list of elements. If you want a list of strongly typed data elements in the config file you can use the PolymorphicConfigurationElementCollection<T> generic which is introduced by the Enterprise Library. This class was specifically designed to store lists of the following form:

<SectionName>
      <add name="blah" type="PluginData strongly typed " xxx="xxxx" xxx="xxxx" .../>

</SectionName>

You have to derive from PolymorphicConfigurationElementCollection and implement the RetrieveElementConfigurationElementType where you retrieve the data type which is responsible for the ser/deserialzation of the other attributes of this node. Error handling code has been omitted. Please have a look at e.g. the Logging Application Block of Entlib to see the correct error handling.

PluginDataCollection.cs

using System;

using System.Collections.Generic;

using System.Text;

using System.Xml;

using System.Configuration;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

namespace EntlibInjection

{

  public class PluginDataCollection
      : PolymorphicConfigurationElementCollection<PluginData>

  {
    /// <summary>

    /// Returns the <see cref="ConfigurationElement"/> type to created for the
    /// current xml node.

    /// </summary>

    /// <remarks>

    /// The <see cref="PluginData"/> include the configuration object type as a
    /// serialized attribute.

    /// </remarks>

    /// <param name="reader"></param>

    protected override Type RetrieveConfigurationElementType(XmlReader reader)

    {
      return typeof(PluginData);
    }
  }

}

I have simplified the RetrieveConfigurationElementType a lot compared to the normally used strongly typed name which should be specified to allow versioning of you confiuration node data types. Please do NOT use this shortcut in production environments. If you do not want strong names for your data types then the PolymorphicConfigurationElementCollection<xxx> is not the right choice for your problem. Instead a direct derivate of ConfigurationElementCollection is the better alternative for you. Do not ask me how to figure out with which version of PluginData you App.Config was created when you do not use strong names for your data types.This can be an important issue if you plan e.g. to create configuration converters from one version to the next. Please have a look at the Logging Block code e.g. TraceListenerDataCollection how the PolymorphicConfigurationElementCollection<xxx> is correctly implemented. Ok so much for versioning. Now we can create our PluginData class which contains the plugin data type and an external config file which contains the plugin configuration. If it is not specified we assume the the plugin configuration is located in the App.config file. This can be  implemented by switching from SystemConfigurationSource to FileConfiguratonSource and put the plugin config file into it.

PluginData.cs

using System;

using System.ComponentModel;

using System.Collections.Generic;

using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

using System.Configuration;

namespace EntlibInjection

{

public class PluginData : NameTypeConfigurationElement

{
public const string PluginTypeProperty = “plugintype”;

public const string PluginConfigProperty = "cfgfile";

private static IDictionary<string, string> emptyAttributes =
    new Dictionary<string, string>(0);

public PluginData()

{}

protected PluginData(string name, Type PluginType)

    : base(name, PluginType)

{
  this.PluginType = this.GetType();
}

[ConfigurationProperty(PluginConfigProperty, DefaultValue = "",
                       IsRequired = false)]

public string ConfigFile

{
  get

  {
    return (string)this[PluginConfigProperty];
  }

  set

  {
    this[PluginConfigProperty] = value;
  }
}

[ConfigurationProperty(PluginTypeProperty, IsRequired = true)]

[TypeConverter(typeof(AssemblyQualifiedTypeNameConverter))]

public Type PluginType

{
  get

  {
    return (Type)this[PluginTypeProperty];
  }

  set

  {
    this[PluginTypeProperty] = value;
  }
}

}

}

To load the configuraton form a config file you can use the FileConfigurationSource from the Enterprise Library and you are ready to rockn roll.

FileConfigurationSource configurationSource = new FileConfigurationSource(“2D.config”);

         PluginManagerConfiguration.GetPluginSettings(configurationSource);

There are many new things within the System.Configuration namespace which this article tries to show you how the can be used. I hope this article gives you enough starting points to jump up on the .NET 2.0 wagon and the new Enterprise Library. One last thing: User settings are not supported by the Enterprise Library. But you can fake this by creating a config file in %APPDATA%\MyEntlibApp\<MyEntlibApp Assembly Version>\User.config and load it from there. To make this work you need a loaded user profile which is not always present e.g.  System Service. In this case you need to load some trusted user profile to make it work.

This article is part of the GWB Archives. Original Author: Alois Kraus

Related Posts