Configuration FAQ
Home About Workshops Articles Writing Talks Books Contact

Frequently Asked Questions

001 Can I rename the config file?
002 What is app.config?
003 I want to give my library a config file.
004 I want to write values to my config file.
005 How do I read the config File?
006 How do I change the config file schema?
007 How do I provide my own configuration file?
008 What are the new configuration features v2.0 of .NET?
009 What are sections and section groups?
010 Why have machine.config and the application config file?

001 Can I rename the config file?

No, not really. The name of the configuration file is determined by the application domain. In most cases you will use the default application domain and so you have to use the default name for the configuration file. Once the application domain has started you cannot change the name of the configuration file. The default name of the configuration file for an application is <process name>.config, so if the application process is app.exe then the configuration file is app.exe.config.

If you want to create a new application domain then you are free to use whatever name you want for the configuration file:

class App : MarshalByRefObject
{
   static void Main(string[] args)
   {
      if (args.Length == 0)
      {
         Console.WriteLine("usage NewConfigName name");
         Console.WriteLine("\t where name is the name of the config file");
         return;
      }
      AppDomainSetup ads = new AppDomainSetup();
      ads.ConfigurationFile = args[0];
      AppDomain ad = AppDomain.CreateDomain("new domain", null, ads);
      // Create a MarshalByRefObject class in the new appdomain and call it
      App app = (App)ad.CreateInstanceAndUnwrap(
         Assembly.GetExecutingAssembly().FullName, "NewConfigName.App");
      app.Run();
   }
   void Run()
   {
      try
      {
         AppSettingsReader apr = new AppSettingsReader();
         String str = (string)apr.GetValue("Data", typeof(string));
         if (str == null)
         {
            Console.WriteLine(
               "cannot find Data value in <appSettings> of {0}",
               AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
         }
         else Console.WriteLine("Data value is \'{0}\'", str);
      }
      catch (Exception)
      {
         Console.WriteLine("cannot find Data value in <appSettings> of {0}",
            AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
      }
   }
}

Here, the AppDomainSetting is initialized with the name of the new config file then this is passed to the CreateDomain to create the application domain. Next an object is created in the new application domain. So that you can access this object across the domain boundary, the object must derive from MarshalByRefObject. The object can then access the configuration file (in this case an entry in the <appSettings> called Data) and the new file will be accessed instead of the default file.

Top

002 What is app.config?

This is a file created for you by Visual Studio. The name of this file is important, and you should not change it. When you build your project Visual Studio will copy all the binary files to the build directory, this includes the process file (.exe) and any private libraries it uses (.dll). In addition, it also copies the app.config file and renames it according to the process name (see 001 for a description of the default name for the config file). If another configuration file is in the build directory then it will be replaced by the copy of app.config. Thus, if you want to make any changes to the configuration file do this to app.config, do not edit the configuration file in the build directory. The reason for this design is quite simple: it aids source control. When you put your project under source control you will not have the build directory, because it is not source code. However, by default, the configuration file must be in the same folder as the process and since the build folder is different to the source code folder, this means that the configuration file must be copied before the application is run.

Top

003 I want to give my library a config file.

You can't. Configuration files are associated with the application domain and every assembly in the application domain will have access to the same configuration file. This means that you cannot have a configuration file for a specific library. The default name of the configuration file is <process name>.config, so if the application process is app.exe then the configuration file is app.exe.config. If you create a new application domain you can change the name of the configuration file. So you could create a new application domain specifically for your library. However, this means that all the public types in the library will have to derive from MarshalByRefObject so that instances can be accessed across the domain boundary. This may not be possible because a type may need to derive from another class and in .NET a class can only have one base class. Also it is a pain for users of your assembly to have to use .NET remoting to access your types. Finally, configuration files are often used to configure system objects (for example, how the diagnostics classes, Trace and Debug, work). It makes sense for there to be a single configuration for such system classes. If you did have per-library configuration then the behaviour of the system classes in the library could be different to other code in the application.

Basically, a configuration file for a library is a bad idea. You have two options. The easiest option is to use the application configuration file. This may mean that you need to inform your users that they need to change the config file, you can get over this by providing an installation program that will do this for you, or write to the config file the first time your code runs. (Remember, that if you write to the configuration file the values are only read the next time an application domain is created, usually this means restarting the process).

The other option is to provide your own configuration file. This is an excellent option because you can use this to provide per user configuration settings.

Top

004 I want to write values to my config file.

There is no support in v1.0 or v1.1 of the framework library to do this, but v2.0 will allow it. However, the config file is just an XML file so really all you need to do is use the FileStream or XmlDocument class to write settings to the file. However, it is not as simple as this, and to understand why you have to understand how values are read from the configuration file.

When you use one of the APIs to read the config file, the call eventually translates into a call to ConfigurationSettings.GetConfig for the section that contains the value. (In general, but not always, this means the XML element.) If this is the first time that the section is read, this method reads the schema and obtains the name of the configuration section handler. The section handler reads the section and creates a a configuration object based on the configuration section and this object is returned from GetConfig (for example, the <appSettings> section a NameValueCollection will be returned). This configuration object is cached in a Hashtable maintained by the application domain, so that when a subsequent call is made to GetConfig for the same section the same configuration object is returned.

As you can see, once the a value from a section has been read, the entire section will be cached in memory; any change to the configuration file will not be reflected when you read a value from the section. This means that in most cases you should treat the configuration file as being read-only. One legitimate use would be to write values to the configuration file when the application closes, so that application settings can be read and used when the application restarts.

Top

005 How do I read the config File?

There are several methods, they are all on classes in the System.Configuration namespace. The simplest to use is AppSettingsReader, create an instance of this to read the <appSettings> section using the GetValue method. These settings:

<configuration>
   <appSettings>
      <add key="Pi" value="3.14"/>
   </appSettings>
</configuration>

can be read with the following code:

double pi;
AppSettingsReader apr = new AppSettingsReader();
// This will read the value from the config file
// and convert it to the requested type
pi = (double)apr.GetValue("Pi", typeof(double));
Console.WriteLine("The value of the Pi key is " + pi);

Note that GetValue takes a type as a parameter, it will attempt to coerce the string value in the configuration file to this type. If the coercion is not possible an exception will be thrown.

The ConfigurationSettings class can also be used to access config file settings. The <appSettings> section is available as a NameValueCollection through the static property AppSettings:

string pi = ConfigurationSettings.AppSettings["Pi"];

Note that the keys and values are strings. Its also important that you remember that access to the <appSettings> section is through a NameValueCollection but the actual items in the <appSettings> section are key-value items.

The most generic way to read a configuration section is to use ConfigurationSettings.GetConfig. This takes a section name in XPATH format to a section under the <configuration> root element. That is, if you want to access a subsection then the section name to use is the parent and subsection names separated by a slash (/). GetConfig then checks the schema to see if there is a handler for the section. The schema is defined in the <configSections> section in machine.config combined with any sections defined in your application's config file. If there is no handler then GetConfig returns null, otherwise the handler is created and passed the configuration section. The handler then creates a configuration object and it is this object that is returned from GetConfig. You can read the <configSections> section to see the type of the section handler, but there is no clue about the type of configuration handler objects returned by the type. The only way to determine this is to suck-it-and-see: call GetConfig and see what object is returned:

static void Main(string[] args)
{
   object o = ConfigurationSettings.GetConfig(args[0]);
   if (o == null)
      Console.WriteLine("no handler");
   else
      Console.WriteLine(o.GetType().ToString());
}

Try this out with runtime, system.diagnostics, appSettings and system.net/settings. (Make sure that you have a config file with an <appSettings> section.) The results should be no handler, Hashtable, ReadOnlyNameValueCollection and NetConfiguration. The <runtime> section returns no configuration object because the configuration handler for this section is IgnoreSectionHandler.

Once you know the configuration object type you can look up its members to see how to access the configuration settings. Note that ReadOnlyNameValueCollection is a private class, but it derives from the public class NameValueCollection and so you can cast the object returned from GetConfig to the base class. NetConfiguration is also a private class, but it has no useful parent class, so effectively this means that the data in this configuration object is inaccessible to you.

Top

006 How do I change the config file schema?

There are two things to do: change the schema in the configuration file and provide a configuration handler class. The simplest case is when there is an existing configuration handler class that suits your needs because then all you need to do is change the configuration file. For example, if you have a section called <mySettings> which is a collection of key-value items, then you can use the NameValueSectionHandler class:

<configSections>
   <section name="mySettings"
      type="System.Configuration.NameValueSectionHandler"/>
</configSections>

If you want this section to be available to every application, then this can be addede to machine.config. However in most cases you'll want it to apply only to your application, so this section must be the first section in your configuration file. Since you know that this handler returns a NameValueCollection you can use the following to access your section:

object o = ConfigurationSettings.GetConfig("mySettings");
NameValueCollection nvc = (NameValueCollection)o;
foreach(string key in nvc.Keys)
{
   Console.WriteLine("{0}={1}", key, nvc[key]);
}

The section in a sample configuration file looks like this:

<mySettings>
   <add key="one" value="1"/>
   <add key="two" value="2"/>
   <add key="three" value="3"/>
</mySettings>

If there is not a suitable handler class then you have to write your own. This class must derive from IConfigurationSectionHandler:

public interface IConfigurationSectionHandler
{
   object Create(object parent, object configContext, XmlNode section);
}

If parent is not null then the handler class should use the values in parent as default values for the settings. The section parameter contains the section that corresponds to the path passed to GetConfig. If this path contains nested elements, then section will have those nested elements too. The handler should then construct a configuration object with this data and then return it. The full name of this class (including its assembly) should then be used in the type attribute of the schema entry. The section can be nested and to do this you need to use a <sectionGroup>.

Top

007 How do I provide my own configuration file?

It makes little sense to use the application's configuration file to store settings for your application. The application configuration file should be used for settings that affect framework objects (like the diagnostics classes) rather than your classes. The reason why the application configuration file is unsuitable is that they are essentially read-only for the application (since the settings are read only once for the application domain) and there is (currently) no mechanism for per-user settings. Furthermore, if you write a library and the library code uses the .NET framework configuration API then the settings will be in the application configuration file, a library cannot have its own configuration file.

There is good reason to use our own configuration file. The framework has very good support for object serialization. The two formatters are BinaryFormatter and SoapFormatter. These classes will serialize an object to stream. If the stream is a FileStream for a file, then the object will be serialized to a file. If you want the user to be able to edit the settings in the file then you should use the SoapFormatter class. The process is straight forward, create a class that has the [Serializable] attribute and then any setting that you want to store in the configuration file should be a field and should be a type that is also serializable. You can provide any other member to the class to help your code access the configuration settings, mst likely by using properties. Your application should create an instance of this type as a static member of a general class in your application, so that all classes have access to it. For example:

[Serializable]
class AppConfig
{
}

class App {
   static void Main()
   {
      // Entry point
   }
   static AppConfig configuration;
   static public AppConfig Configuration
   {
      get
      {
         if (configuration == null)
         {
            SoapFormatter sf = new SoapFormatter();
            using(FileStream fs = File.OpenRead("myconfig.soap"))
            {
               configuration = (AppConfig)sf.Deserialize(fs);
            }
         }
         return configuration;
      }
 }

However, the SOAP schema is quirky. A better option is to use the XmlSerializer class. This class is essentially a bridge between a serializable object and XML. The idea is that you use attributes to indicate what XML items are used to serialize a particular member of the serializable class. This class is then used in the constructor of the XmlSerializer class which can then take instances of the serializable class and serialize them to a stream. For example, the following gives you access to a file in the same format as the application's configuration file:

[XmlRoot("configuration", IsNullable=false)]
public class Configuration
{
   // The <appSettings> collection
   [XmlArray("appSettings"), XmlArrayItem("add", typeof(AppSetting))]
   public AppSetting[] Items;
   // The rest of the config file
   [XmlAnyElement]
   public XmlElement[] Unrecognized;
   public Configuration(){}
}

// This represents an element in the <appSettings> collection
public class AppSetting
{
   public AppSetting(){}
   public AppSetting(string k, string v){key=k; value=v;}
   [XmlAttribute()] public string key;
   [XmlAttribute()] public string value;
}

The first attribute indicates that the root is <configuration> and within the root will be a section called <appSettings> that is a collection of <add> elements. Each item is represented by an AppSettings object, which contains a key and a value. Any other item in the config file can be accessed through the Unrecognized member.

Using this class is straightforward:

static public void Save(Configuration config)
{
   XmlSerializer ser = new XmlSerializer(typeof(Configuration));
   string f = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
   using(FileStream fs = File.Open(f, FileMode.Create))
   {
      XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
      ns.Add("", "");
      ser.Serialize(fs, config, ns);
   }
}
static public Configuration Load()
{
   XmlSerializer ser = new XmlSerializer(typeof(Configuration));
   string f = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
   using(FileStream fs = File.OpenRead(f))
   {
      return (Configuration)ser.Deserialize(fs);
   }
}

Of course, your configuration object can be any type you like, it does not have to serialize to the application configuration file! To create your own serializable class you can use the xsd tool. Basically, all you need to do is create an XML file with the data that you want in the config file and then run xsd to generate the .xsd schema file. You can then use xsd to generate code with the /c and /l switches (/l gives the language generated, and of course you should use /l:cs to get C# generated).

Since you now have control over what is written and what is read, but you also have control over where the data resides. If you store the data in the application folder then you will benefit from XCOPY deployment: since the configuration files are in the same folder as the code files it means that deploying the application is as simple as copying the files. One option here is to name the configuration file according to the name of the logged on user (which you can obtain through Environment.UserName) so that each user will have their own settings.

However, if you really want to have per-user settings you really should store the settings in a user's own folder. You can access this though Environment.GetFolderPath and pass SpecialFolder.LocalApplicationData to get the folder for a non-roaming user and SpecialFolder.ApplicationData to get the folder for a roaming user. The user, of course, has access to the file and so is free to edit the file. However, you no longer get the benefit of XCOPY deployment because when you deploy the application to another machine you have to make sure that you specifically set up configuration files for the users. Of course, the solution here is to provide default values for the configuration object so that the configuration file is created when the object is serialized (most likely at application shutdown).

You can also use isolated storage to store the configuration file, and this has the benefit of allowing only the user (or the administrator) to edit the file, but has the disadvantage that the file is in a separate location to the application.

Top

008 What are the new configuration features v2.0 of .NET?

In short: classes to write to the configuration file, as well as support for per user settings, so that you have a config file for the application assembly stored under the user's profile. The per user settings are interesting: the default values are in machine.config, these are overridden by values in the application's configuration file, and those are overridden by values in the per-user configuration file.

Top

009 What are sections and section groups?

ConfigurationSettings.GetConfig takes a string which identifies the name of the section. If the section is nested in another element then you need to give an XPATH-like path, that is the section names separated with a slash (/). Only a section identified with a <section> element can have a configuration section handler. To give a hierarchy of sections you can use <sectionGroup>. For example:

<sectionGroup name="outer">
   <sectionGroup name="middle">
      <section name="inner" type="System.Configuration.NameValueSectionHandler" />
   </sectionGroup>
</sectionGroup>

To get access to inner you would use GetConfig("outer/middle/inner"). Note that GetConfig("outer/middle") or GetConfig("outer") will return no configuration objects because neither of these sections have a section handler defined.

Top

010 Why have machine.config and the application config file?

Put simply machine.config has default settings for all applications on your machine, and the application configuration file has settings just for the application. The application settings override the settings in machine.config.

The technical explanation requires looking at the Create method of IConfigurationSectionHandler. This method is implemented by all configuration section handlers and looks like this:

object Create(object parent, object configContext, XmlNode section);

The section parameter is the XML for that section, the configContext parameter is only relevant to ASP.NET and will be ignored here. GetConfig will call this twice: once for the section in machine.config and then once more for the section in the application config file. The handler should process the XML and use this information to create a configuration object which is returned from Create. If the call to Create for machine.config returns a configuration object, then this object is passed as the parent parameter to the subsequent call to Create for the application config file. It's entirely up to the handler to determine what it must do with this parent object, but clearly the most logical thing to do is to use the values as default values and if there are equivalent values in the application configuration file, then the parent values will be overwritten.

Top

This page is (c) 2006 Richard Grimes, all rights reserved