Storing Configuration Settings in .NET Configuration Files

.Config file storage is the most common usage scenario for configuration information, as it's integrated with the .NET framework and respects auto-detection of changes in ASP.NET applications even for externally stored configuration files.

Default Configuration

The AppConfiguration class uses the ConfigurationFileConfigurationProvider class by default stores configuration values in the application's default configuration file in a section with the same name as the configuration class.

<MyAppConfig>
  <add key="ApplicationTitle" value="West Wind Web Toolkit Sample" />
  <add key="DebugMode" value="ApplicationErrorMessage" />
  <add key="MaxPageItems" value="20" />
</MyAppConfig>

For custom sections the provider automatically creates the approppriate section configuration required by .NET to read the section.

You can override the section and filename with the ConfigurationSection and ConfigurationFile properties on the provider respectively.

You can also store settings in .NET's own appSettings key by setting the ConfigurationSection property to "appSettings" or explicitly setting it to null or empty string.

Customizing the ConfigurationFile Provider

To customize configuration settings, you can set properties on the Configuration provider. Available customization options on the ConfigurationFileConfigurationProvider are:

  • **ConfigurationSection **
    Optional configuration section name. If not specified the default appSettings section is used. Recommend you always set this to isolate your application's configuration settings clearly. By default this value is same name as the configuration class.

  • ConfigurationFile
    Optional external file to store config data in. This externalizes the configuration information for easier management and isolation in source control scenarios.

  • PropertiesToEncrypt
    Optional comma delimited list of properties to encrypt.

  • EncryptionKey
    Optional encryption key for the encrypted properties. If not specified when properties are to be encrypted a default key is used. Recommend you always set PropertiesToEncrypt and Encryption key at the same time.

To set these properties you can:

  • Pass a new Provider instance to Initialize() to your Config class
  • Create an overloaded OnCreateDefaultProvider() method on your Config class

Pass a customize Provider to Initialize()

This step is simple but requires some logic in your application code:

var provider = new ConfigurationFileConfigurationProvider<CustomConfig>()
{                    
   //ConfigurationFile = "CustomConfiguration.config",
   ConfigurationSection = "MyApplicationConfiguration",
   EncryptionKey = "ultra-seekrit",  // use a generated value here
   PropertiesToEncrypt = "Password,AppConnectionString"
};

var config = new CustomConfig();
config.Initialize(provider);

In this code you create a new instance of the ConfigurationFileConfigurationProvider class and pass in a generic argument of your custom configuration class (ie. CustomConfig). You can then set properties on the provider - here I use a custom configuration section, and choose to encrypt two of the configuration properties of my class.

This is an easy way to create a new provider, but it's external to the actual configuration logic.

Add OnCreateDefaultProvider() Method to your Configuration Class

Typically you only need one way to access configuration information, so it's actually a better idea to encapsulate the default provider configuration as part of your custom class.

To create custom configurations the AppConfiguration class includes two override points: OnInitialize() and OnCreateDefaultProvider(). The latter zeros in only on provider creation while the former needs to handle both provider creation and initial loading of the configuration values (typically via the Read() method).

In most cases overriding OnCreateDefaultProvider() is sufficient. Here's an example implementation:

public class CustomConfigConfiguration : 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; }

    public CustomConfigFileConfiguration()
    {
        ApplicationName = "Configuration Tests";
        DebugMode = DebugModes.Default;
        MaxDisplayListItems = 15;
        SendAdminEmailConfirmations = false;
        Password = "seekrit";
        AppConnectionString = "server=.;database=hosers;uid=bozo;pwd=seekrit;";
    }

    protected override IConfigurationProvider OnCreateDefaultProvider(string sectionName, object configData)
    {
        var provider = new ConfigurationFileConfigurationProvider<CustomConfigConfiguration>()
        {
            //ConfigurationFile = "CustomConfiguration.config",
            ConfigurationSection = sectionName,
            EncryptionKey = "ultra-seekrit",  // use a generated value here
            PropertiesToEncrypt = "Password,AppConnectionString"
        };

        return provider;
    }        
}

The key is the overridden OnCreateDefaultProvider() method which creates the provider and returns it. The method receives two optional parameters which are forwarded from the Initialize() method. The sectionname will be set to a default value or the value the user provided.

The configData parameter is optional and allows passing custom information from the Initialize() method down to the provider. This is useful if there are additional requirements for initialization - for example, you could pass down the name of a configuration to store the data into in configData.

Writing to Config Files

Configuration files can also be written to with the ConfigurationFileConfigurationProvider assuming the application has write access to the configuration file.

To write you can simply call the .Write() method on the configuration object:

var config = new CustomConfigConfiguration();
config.Initialize();
            
config.MaxDisplayListItems = 12;
config.DebugMode = DebugModes.DeveloperErrorMessage;

config.Write();

This writes the config object's current property state into the configuration store.

Write() and Permissions

Write operations on configuration files require that the host process has permissions to write to the config file, which is not a given in many applications.

Web Applications that want to write to web.config or external config files need WRITE access for the Application Pool user account (NETWORK SERVICE for example). By default the Web user has no rights to write web.config, so if you want to be able to update settings through the Web interface you'll need to give permissions to this account. Be aware that this can have other security implications in case you're hacked.

Desktop application with UAC enabled by default also don't have access to their .config files if they are stored in the installation folder.

Failed Write() operations fail silently - you need to check the boolean result value from Write() to detect errors and you can look at the ErrorMessage property to determine what the error is.

Note that AppConfiguration also tries to implicitly write any missing values to the .config file after load if the section doesn't exist or individual values are not found. The store is checked and compared on Read() and the full store is always written when Write() is used.

Successful Write() operations to web.config in Web applications cause the Web application's AppDomain to restart so you'll want to write sparingly.


© West Wind Technologies, 2019 • Updated: 12/19/15
Comment or report problem with topic