Many years ago, when I was just starting out with .NET (in the 1.0 days - now nearly 10 years ago), I wrote an article about an Application Configuration component I created to improve application configuration using strongly typed classes. To me, application configuration is something that I've been adamant about since - well forever, even pre-dating .NET. An easy to access configuration store along with an easy application access mechanism to access and maintain configuration settings through code are crucial to building applications that are adaptable. I found that the default configuration features built into .NET are pretty good, but they still take a bit of effort to maintain and manage.

So I built my own long ago, with focus on a code-first approach. The configuration library I created is small, low-impact and simple to use - you create a class and add properties, instantiate and fire away at configuration values. The library has been updated and majorly refactored a few times over the years to adapt to changes in .NET and common usage patterns. In this post I'll describe the latest updated version of this library as I think many of you might find it useful.

The very latest version of this library is now available on its own as a smaller self contained component. You can find the actual files and basic usage info here:

But before I jump in and describe the library lets spend a few minutes reviewing what configuration options are available in the box in .NET and why I think it made sense to maintain a custom configuration solution over the years.

Configuration? How boring, but…

I consider configuration information a vital component of any application and I use it extensively for allowing customization of the application both at runtime and through external configuration settings typically using .config files and occasionally in shared environments with settings stored in a database. The trick to effective configuration in any application is to make creating new configuration values and using them in your application drop dead easy. To me the easiest way to do this is by simply creating a class that holds configuration values, along with a mechanism for easily serializing that configuration data. You shouldn't have to think about configuration - it should just work like just about any other class in your projects :-)

In my applications, I try to make as many options as possible user configurable and configure everything from user application settings, to administrative configuration details, to some top level business logic options all the way to developer options that allow me to do things like switch in and out of detailed debug modes, turn on logging or tracing and so on. Configuration information can be varied so it should also be easy to have multiple configuration stores and switch between them easily.

This is not exactly a sexy feature, but one that is quite vital to the usability and especially the configurability of an application. If I have to think about setting or using of configuration data too much, have to remember every setting in my head (ie. "magic strings"), or have to write a bunch of code to retrieve values, I'll end up not using them as much as I should, and consequently end up with an application that isn’t as configurable as it could be.

What does .NET provide for Configuration Management?

So what do you use for configuration? If you're like most like developers you probably rely on the AppSettings class which provides single level configuration values at the appSettings key. You know the kind that's stored in your web.config or application.config file:

<configuration>
  <appSettings>
    <add key="ApplicationTitle" value="Configuration Sample (default)" />
    <add key="ApplicationSubTitle" value="Making ASP.NET Easier to use" />
    <add key="DebugMode" value="Default" />
    <add key="MaxPageItems" value="0" />
appSettings> configuration>

All the configuration setting values are stored in string format in the appSettings section of an application's configuration file.

To access settings, the System.Configuration assembly in .NET provides a fairly easy way to access these configuration values via code from within applications:

string applicationTitle = ConfigurationSettings.AppSettings["ApplicationTitle"];

Easy enough, right? But it gets a little more complex if you need to grab a value that's not a string. For numeric or enum values you need to first ensure the value exists (is non-null) and then convert the string explicitly to whatever type, since configuration values are always strings. Here's what this looks like:

int maxPageItems = 0;
string tInt = ConfigurationManager.AppSettings["MaxPageItems"];
if (tInt != null)
    int.TryParse(tInt, out maxPageItems);

DebugModes mode = DebugModes.Default;
string tenum = ConfigurationManager.AppSettings["DebugMode"];
if (tenum != null)    
    Enum.TryParse(tenum,out mode);

which is a bit verbose if you have to go through this every time you want to use a configuration value. You also have to remember the values and use the ConfigurationManager.AppSettings class. Minor but 'noisy' in application level code.

AppSettings values are also limited to a single AppSettings section inside of an application's or Web config file. Luckily you can also create custom configuration sections use the same configuration format in custom sections in a config file as long as those custom sections get declared:

<configuration>
  <configSections>
    <section name="CustomConfiguration" requirePermission="false" 
type="System.Configuration.NameValueSectionHandler,System,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> configSections> <CustomConfiguration> <add key="ApplicationName" value="Configuration Tests" /> <add key="DebugMode" value="Default" /> <add key="MaxDisplayListItems" value="15" /> <add key="SendAdminEmailConfirmations" value="False" /> <add key="MailServer" value="3v7daoNQzllLoX0yJE2weBlljCp0MgyY8/DVkRijRTI=" /> <add key="MailServerPassword" value="ud+2+RJyqPifhK4FXm3leg==" /> CustomConfiguration> configuration>

You can then access a custom section with:

var settings = ConfigurationManager.GetSection("CustomConfiguration") as NameValueCollection;            
Console.WriteLine(settings["ApplicationName"]);

and essentially get the same behavior as you get with the AppSettings keys. The collection you get back is a read-only NameValueCollection that's easy to run through and read from.

.NET's configuration provider also supports strongly typed configuration sections via code, which involves creating classes based base on the ConfigurationSection class. This gives you a slightly different configuration format that's a little less verbose than the add/key/value structure of NameValue type configuration:

xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="MyCustomConfiguration" requirePermission="false"
             type="Westwind.Utilities.Configuration.MyCustomConfigurationSection,Westwind.Utilities.Configuration.Tests"/>
  configSections>
  <MyCustomConfiguration 
        ApplicationName="Configuration Tests"  
        MaxDisplayItems="25"
        DebugMode ="ApplicationErrorMessage"         
        />
configuration>

This is a little more involved in that you need to define a class and define each property along with some inherited logic to retrieve the configuration value.

class MyCustomConfigurationSection : ConfigurationSection
{        
    [ConfigurationProperty("ApplicationName")]
    public string ApplicationName
    {
        get { return (string) this["ApplicationName"]; }
        set { this["ApplicationName"] = value; }
    }

    [ConfigurationProperty("MaxDisplayItems",DefaultValue=15)]
    public int MaxDisplayItems
    {
        get { return (int) this["MaxDisplayItems"]; }
        set { this["MaxDisplayItems"] = value; }
    }

    [ConfigurationProperty("DebugMode")]
    public DebugModes DebugMode
    {
        get { return (DebugModes) this["DebugMode"]; }
        set { this["DebugMode"] = value; }
    }                
}

but the advantage is that you can reference the class as a strongly typed class in your application. With a bit of work you can even get Intellisense to work on your configuration settings inside of the configuration file. You can find out more from this detailed article from Rob Seeder. Strongly typed configuration classes are useful for static components that have lots of configuration settings, but for typical dynamic configuration settings that frequently change in applications the more dynamic section of key value pairs is more flexible and easier to work with dynamically.

For certain kinds of desktop applications, Visual Studio can also create a strongly typed Settings class. If you create a WinForms or WPF project for example it adds a Settings.settings file, which lets you visually assign properties in a designer. When saved the designer creates a class that accesses the AppSettings values indirectly.

SettingsDesigner

This is pretty nice in that it keeps all configuration information inside of a class that is managed for you as you add values. You also get default values and you can easily use the class in code:

var settings = new Settings();
var mode = Settings.DebugMode;
MessageBox.Show(mode.ToString());

The class is strongly typed and internally simply references a custom configuration section of values that are read from the config file. This is a nice feature, but it's limited to desktop Windows applications Console, WinForms, WPF and Windows 8 applications. It's also limited to a single configuration class.

Writing Values to the Config File

.NET also has support for writing configuration values back into configuration files via the configuration manager. You can load up a configuration, load a section and make changes to it, then write the entire configuration back out to disk assuming you have permissions to do this.

var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var section = config.GetSection("appSettings") as AppSettingsSection;            
section.Settings.Add("NewKey", "Value");
config.Save();

This also works for Web Applications where you can use:

var config = WebConfigurationManager.OpenWebConfiguration("~");

to read the top level configuration.

Permissions are crucial here and often you will not be able to write configuration back using this approach. For Web applications Full Trust and read/write access to the web.config file are required. For desktop applications write file rights in the folder are required,  which is often not available - with User Account Control on you typically don't have rights to write into the Configuration folder.

Clearly there are a lot of choices available in .NET to handle configuration storage and retrieval. It's great that the ConfigurationManager is available to provide base features to create simple configuration storage quickly.

Creating a better Application Configuration Class

Personally though, I prefer a more structured approach for configuration management in my applications. Like everything else in my applications I expect my configuration settings to be based on one or more classes that I can simply add properties to and persist that data easily in my application.

There are some native choices available for that - after all .NET includes easy to use tools for serializing to XML and JSON. It's pretty trivial to create some code to arbitrarily take a class and serialize it. However, wouldn't it be nice if the format was easily switchable and if you didn't have to worry about writing out the data yourself?

When I created the ApplicationConfiguration component years ago that was my goal. The current incarnation of the Westwind ApplicationConfiguration library provides the following features:

  • Strongly typed Configuration Classes
  • Simply create a class and add Properties
  • Automatic type conversion for configuration values
  • Default values so you never have to worry about read failures
  • Automatic synching of class and configuration store if values are missing
  • Easily usable from any kind of .NET application or component
  • Support for multiple configuration objects
  • Multiple configuration formats
    • Standard .NET config files
      • Custom Sections
      • External Config files
      • AppSettings
    • Standalone XML files (XML Serialization)
    • Strings
    • Sql Server Tables
    • Customizable with easy to create ConfigurationProviders

How to use the AppConfiguration Class

The core of the Westwind Application Configuration library relies on a configuration class that you implement simply by inheriting from the Westwind.Utilities.Configuration.AppConfiguration class. This base class provides the core features for reading and writing configuration values that are properties of the class that you create. You simply create properties and instantiate the class and call Initialize() to initially assign the provider and load the initial configuration data.

To create a configuration class is as easy as creating a class and adding properties:

class MyConfiguration : Westwind.Utilities.Configuration.AppConfiguration
{
    public string ApplicationName { get; set; }
    public DebugModes DebugMode { get; set; }
    public int MaxDisplayListItems { get; set; }
    public bool SendAdminEmailConfirmations { get; set; }
    public string MailServer { get; set; }
    public string MailServerPassword { get; set; }

    public MyConfiguration()
    {
        ApplicationName = "Configuration Tests";
        DebugMode = DebugModes.Default;
        MaxDisplayListItems = 15;
        SendAdminEmailConfirmations = false;
        MailServer = "mail.MyWickedServer.com:334";
        MailServerPassword = "seekrity";
    }
}

To use the configuration class you can then simply instantiate the class and call Initialize() with no parameters to get the default provider behavior and then fire away at the configuration values with the class properties:

var config = new MyConfiguration();
config.Initialize();

// Read values
string appName = config.ApplicationName;
DebugModes mode = config.DebugMode;
int maxItems = config.MaxDisplayListItems;

Note that the Initialize() method should always be called on a new instance to initialize the configuration class. Initialize() internally assigns the provider and reads the initial configuration data from a store like the configuration file/section.

Once the class is instantiated and initialized you can go ahead and read values from the class. The values are loaded only once during Initialize() (or Read() if you decide to re-read settings manually) and are cached in the properties after the initial load. The values of the properties reflect the values of the configuration store - here from the application's config or web.config file, in a MyConfiguration section.

If the configuration file or section or values don't exist and the file is writable the releavant .config file is created. The content of the file looks like this:

<configuration>
  <configSections>
    <section name="MyConfiguration" requirePermission="false" 
type="System.Configuration.NameValueSectionHandler,System,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> configSections> <MyConfiguration> <add key="ApplicationName" value="Configuration Tests" /> <add key="DebugMode" value="Default" /> <add key="MaxDisplayListItems" value="15" /> <add key="SendAdminEmailConfirmations" value="False" /> <add key="MailServer" value="mail.MyWickedServer.com:334" /> <add key="MailServerPassword" value="seekrity" /> MyConfiguration> configuration>

Note that a custom Section is created in the config with standard key values. The Initialize() method also takes an optional sectionName parameter that lets you explicitly override the section name. You can also use appSettings as the section name in which case the standard appSettings section is used without any custom configuration section configuration.

In the code above the configuration section is written automatically as part of the Initialize() code - but you can also explicitly write out configuration information using the Write() method:

var config = new MyConfiguration();
config.Initialize();

config.DebugMode = DebugModes.ApplicationErrorMessage;
config.MaxDisplayListItems = 20;

config.Write();

The key with calling the Write() method is that you have to have the permissions to write to the configuration store. For example, typical Web applications don't have rights to write to web.config unless you give explicit Write permissions to the file to the Web user account. Likewise, typical Windows applications installed in the Program Files folder can't write to files in the installation folder due to User Account Control permissions, unless you explicitly add rights for the user to write there. Location matters, so it's important to understand your environment before writing configuration values or expecting them to auto-created when initializing.

Using the Configuration Class as a Singleton

Because configuration data tends to be fairly static in most applications, it's a not a good idea to instantiate the Configuration class every time you need to access the configuration data. It's fairly expensive to read a file from disk, or access a database and the deserialize the values from the configuration into an object. It's much better to set up the configuration class once at application startup or using a static property to keep an active instance of the configuration class around.

Personally I prefer the latter by using a 'global' application object that I tend to have in every application. I can attach the configuration object on this application class as a static property. The advantage of a static property on a 'global' object is that it's portable and I can stick it into my business layer and use it in a Web app, a service or desktop application without any changes (at least when using config files). In Web applications static properties are also available to all threads so many simultaneous Web requests can share configuration information from the single instance.

To create a static Singleton is easy with code like this:

public class App
{
    public static MyConfiguration Configuration { get; set; }

    static App()
    {
        Configuration = new MyConfiguration();
        Configuration.Initialize();
    }
}

Now anytime you need access to the configuration class you can simply use:

DebugModes mode = App.Configuration.DebugMode;
int maxItems = App.Configuration.MaxDisplayListItems;

You never need to worry about instantiating the configuration class in your application code, it's just always there and cached to boot.

Using and customizing  Configuration Providers

So far I've used only the default provider which is the ConfigurationFileConfigurationProvider class using default options, which use the standard .NET application configuration file and a section with the same name as the class within it. This means yourexe.exe.config or web.config for Web applications typically.

The default provider behavior using the ConfigurationFileConfigurationProvider is the most likely use case for the configuration configuration, but you can certainly customize the provider or even the behavior of the a provider by passing a custom provider to the Initialize() method. Initialize() takes a parameter for a Provider instance, a section name and arbitrary configData.

For example to use a custom section in the default configuration file you can specify the sectionName parameter in Initialize():

var config = new MyConfiguration();
config.Initialize(sectionName: "MyAdminConfiguration");

Of course you can also pass in a completely configured ConfigurationProvider instance which allows you to set all the options available on a provider:

var config = new AutoConfigFileConfiguration();

// Create a customized provider to set provider options
var provider = new ConfigurationFileConfigurationProvider<AutoConfigFileConfiguration>()
{
    ConfigurationSection = "MyCustomConfiguration",
    EncryptionKey = "seekrit123",
    PropertiesToEncrypt = "MailServer,MailServerPassword"                
};

config.Initialize(provider);  
            
// Config File and custom section should exist
string text = File.ReadAllText(TestHelpers.GetTestConfigFilePath());

Assert.IsFalse(string.IsNullOrEmpty(text));
Assert.IsTrue(text.Contains(""));

You can also opt to use a completely different provider than the ConfigurationFileConfigurationProvider used so far in the examples. It's easy to create a provider instance and assign it during initialization, but realistically you'll want to embed that default logic directly into the provider itself so the logic to instantiate is encapsulated within the provider itself.

The following is an example of a configuration class that defaults to a database provider:

public class DatabaseConfiguration : Westwind.Utilities.Configuration.AppConfiguration
{
    public string ApplicationName { get; set; }
    public DebugModes DebugMode { get; set; }
    public int MaxDisplayListItems { get; set; }
    public bool SendAdminEmailConfirmations { get; set; }
    public string Password { get; set; }
    public string AppConnectionString { get; set; }

    // Must implement public default constructor
    public DatabaseConfiguration()
    {
        ApplicationName = "Configuration Tests";
        DebugMode = DebugModes.Default;
        MaxDisplayListItems = 15;
        SendAdminEmailConfirmations = false;
        Password = "seekrit";
        AppConnectionString = "server=.;database=hosers;uid=bozo;pwd=seekrit;";
    }
    
    ///// 
    ///// Override this method to create the custom default provider - in this case a database
    ///// provider with a few options.
    ///// 
    protected override IConfigurationProvider OnCreateDefaultProvider(string sectionName, object configData)
    {
        string connectionString = "LocalDatabaseConnection";
        string tableName = "ConfigurationData";

        var provider = new SqlServerConfigurationProvider<DatabaseConfiguration>()
            {
                Key = 0,
                ConnectionString = connectionString,
                Tablename = tableName,
                ProviderName = "System.Data.SqlServerCe.4.0",
                EncryptionKey = "ultra-seekrit", // use a generated value here
                PropertiesToEncrypt = "Password,AppConnectionString"
            };

        return provider;
    }
}

This class implements the OnCreateDefaultProvider() method which is overridden to provide… a customized provider instance. The method receives a section name (which may or may not be used) and an optional configData parameter. configData can contain any arbitrary data that you can pass to the Initialize() method. For example, you might pass in a connection string value, or an anonymous object that contains both the connection string and table name that are hardcoded into the method above.

By implementing the above method the default behavior now loads the database provider, but you can still override the provider by explicitly passing one into the Initialize() method.

Providers really are the key to the functionality provided by the Application Configuration library - they're the work horses that do the work of retrieving and storing configuration data. The Westwind Application Configuration library consists of the AppConfiguration base class, plus a host of configuration providers that provide the actual logic of reading and writing of configuration values.

Here's the overall class layout:

AppConfiguration

Your class inherits from AppConfiguration which in turn contains a configuration provider instance. The instance is created during the Initialize() call - either using the default Configuration File provider or the custom provider passed in, or the provider you explicitly implement in OnCreateDefaultProvider(). The providers then implement the Read() and Write() methods responsible for retrieving the configuration data.

Configuration File Provider

The configuration file provider uses the .NET ConfigurationManager API to read values and direct XML DOM manipulation to add values to the config file. I opted for using the DOM rather than that ConfigurationManager to write values out, as there are fewer permissions issues. The Configuration API requires Full Trust to write because it has access to machine level configuration and using XML DOM and file IO allows writing of config files as long as the file permission are valid and it can work even in Medium trust. Configuration values are read one at a time and populated on the object in place, which means that if you use this you can call Initialize() as part of the constructor and automate instantiation without requiring a separate call to Initialize() from application code.

The configuration file provider allows for writing configuration files in separate locations, in customized sections as well as using the standard appSettings section. The default is to use the normal application configuration file in a section with the same name as the class.

Key Properties:

  • ConfigurationFile
  • ConfigurationSection

XmlFileConfigurationProvider

At first glance the XML file configuration provider sounds a lot like the ConfigurationFile provider. Both write output into files and use XML, but the XML provider is separate from .NET's configuration file system. This means configuration values written out to file don't automatically notify the calling app of changes. The XML File provider also relies on standard .NET serialization to produce the file output, which means that you can save much more complex data in a configuration class than with configuration file sections, which require a flat object structure.

XML files allow for more complex structure as you can go directly from object to serialized XML output. So if your configuration data includes nested data or needs to track collections of values, the XML Configuration provider can be a much better choice than .config files. This provider uses XML serialization to write XML directly to and from files.

Key Properties:

  • XmlConfigurationFile

SqlServer Configuration Provider

The SQL Server configuration provider stores configuration information in a SQL Server table with two fields: A numeric Id and a single text field that contains an XML serialized string. Multiple sets of configuration values can be stored in configuration table using data rows based on the Id. The provider works with SQL Server and SQL Server Compact and may also work with other providers (not tested). To use this provider you provide a connection string to the database plus an optional table name and an optional Id value for the configuration. The table name defaults to ConfigurationSettings and that table has an integer Id and ConfigData text field.

This provider also relies on XML Serialization - it attempts to read the data from the database, if it doesn't exist creates the table and inserts the value. You can specify an Id number that identifies the configuration instance - so you can create multiple configuration classes and map them to separate records in the configuration table.

Key Properties:

  • ConnectionString (Connection string or Connection Strings Entry)
  • Tablename
  • ProviderName (ADO.NET Provider name)
  • Key (integer Id for configuration record)

String Configuration Provider

String serialization is mostly useful to capture the configuration data and push it into some alternate and unsupported storage mechanism. Some applications might store configuration data entirely in memory, or maybe the configuration data is user specific and can live in the ASP.NET Session store for example.

This provider is largely unnecessary as string serialization is built directly into the core AppConfiguration class itself. You can use assign XML data to the config object with config.Read(string xml) to read configuration values from an XML string in XmlSerialization format and use the WriteAsString() method to produce a serialized XML string.

Key Property:

  • InitialStringData (the initial string to deserialize from)

Configuration Property Encryption

The base configuration provider also allows for encryption of individual configuration properties. Rather than encrypting an entire configuration section it's possible to encrypt only certain sensitive values like passwords and connection strings etc. which makes it easy to change most keys in a configuration file as needed, and leave a few sensitive values to be encrypted either on a development machine or via a Write() operation of the configuration.

To create encrypted keys specify the PropertiesToEncrypt property with a comma delimited list of properties that are to be encrypted. You also need to provide a string encryption key, which is used to handle the two-way encryption:

var provider = new ConfigurationFileConfigurationProvider<MyConfiguration>()
{
    ConfigurationSection = sectionName,
    EncryptionKey = "ultra-seekrit",  // recommend to use a generated value here
    PropertiesToEncrypt = "Password,AppConnectionString"
};

This provider produces a section in the configuration file that looks like this:

<MyConfiguration>
  <add key="ApplicationName" value="Changed" />
  <add key="DebugMode" value="DeveloperErrorMessage" />
  <add key="MaxDisplayListItems" value="12" />
  <add key="SendAdminEmailConfirmations" value="True" />
  <add key="Password" value="ADoCNO6L1HIm8V7TyI4deg==" />
  <add key="AppConnectionString" value="z6+T5mzXbtJBEgWqpQNYbBss0csbtw2b/qdge7PUixE=" />
MyConfiguration>

When the AppConfiguration class reads the values from the configuration file (or other configuration store), the values are automatically decrypted so the configuration properties are always unencrypted when accessed. The Write() operation writes out the encrypted values into the configuration file. As you can see, using encryption only works if you can somehow write to the file, otherwise the encrypted values never are stored in the configuration. This means you need to have permissions to write to the file, either at development time to create the original values, or on the live site.

Writing Values to the Configuration Store - Permissions

Application level configuration is pretty nice and because the Configuration class is just a plain class it's easy to create an updatable configuration management interface in your applications. You can bascially display and capture configuration values directly from the UI via Databinding or ModelBinding and then simply call config.Write() to write out configuration data to the configuration store. It's easy to do. For example, in an MVC application I can simply have a Configuration Controller Action and View that displays and captures the configuration data directly of the Configuration object. You can update the values in the UI and then simply call the Write() method to write the configuration data out to the store.

The key is that you have to have permissions for this to work. If you store configuration settings in web.config you need to give rights to Web account to be able to write the file. For Web applications it might actually be better to use an external configuration file for the configuration settings to avoid having to explicitly give write access to web.config. Similar issues might be necessary in some desktop scenarios - rather than writing configuration information into a file in the installation/execution folder of an application, read and write the configuration data to a file located in My Documents or AppData where the logged on user has full access.

Summary

Configuration is an important part for just about any application, and this component has been very useful to me over the years, making it an absolute no brainer to just drop in a configuration class into just about any application I build. As I go along during development, just about any parameterizable setting gets added to one or more configuration classes. In most of my applications I have an application level configuration class that holds app specific settings like customizable messages, sizes, measurements, default values etc. as well as an Admin specific configuration that holds things like mail server and sender information, logging options, debugging and profiling options etc.. In Web applications in particular it's super nice to make these kind of changes in web.config files, and have the change immediately take effect. It's a very satisfying experience.

Recently I took the time to clean up this component a bit and extract it from the West Wind Web Toolkit where's it's been living for some time in obscurity. It's still in the toolkit and its forthcoming new version, but I figured pulling it out as a standalone component and sharing it on GitHub might give a little more attention to this useful component. I hope some of you find it useful.

Resources