I think many who have worked with .NET Framework are familiar with the situation where you need to read the App.config configuration file, and it’s not a particularly pleasant task. You always have to create a bunch of static classes, which essentially refer to ConfigurationManager.AppSettings. Or, writing complex and unclear custom configuration sections, inserting each section into the configSection with a reference to the section’s class and its assembly, which isn’t ideal either. When compared to .NET Core, which can deserialize JSON and map it to an object, the situation with the regular .NET Framework looks discouraging.

It often comes down to the following code:

using System;
using System.Configuration;

namespace ConConfig
{
    public static class Config {
        public static int Count => int.Parse(ConfigurationManager.AppSettings.Get("Count"));
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Count: " + Config.Count);
            Console.ReadLine();
        }
    }
}

And the corresponding configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
    <appSettings>
        <add key="Count" value="0" />
        <add key="AnotherKey" value="1" />
        <add key="SomeAnotherKey" value="2" />
    </appSettings>
</configuration>

This is undoubtedly a simple and effective approach, provided you only have 5-10 parameters. However, this approach also leads to the creation of static classes, which means that testing such code becomes much more difficult, which is also a downside. Of course, as an alternative, you can always use an external file in any format, but in my opinion, this is often overkill.

Fortunately, there is a solution that allows you to use standard configuration files similarly to how configuration works in .NET Core. The name of this lifesaving library is West Wind Application Configuration.

The code above can be rewritten as follows:

using System;
using System.Configuration;
using Westwind.Utilities.Configuration;

namespace ConConfig
{
    public class AwesomeConfig : AppConfiguration
    {
        public int Count { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var config = AwesomeConfig();
            config.Initialize();
            Console.WriteLine("Count: " + config.Count);
            Console.ReadLine();
       }
    }
}

And the configuration file like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="AwesomeConfig" requirePermission="false" type="System.Configuration.NameValueSectionHandler,System,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        <section name="AnotherSection" requirePermission="false" type="System.Configuration.NameValueSectionHandler,System,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </configSections>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
    <AwesomeConfig>
        <add key="Count" value="0" />
    </AwesomeConfig>
    <AnotherSection>
        <add key="AnotherKey" value="1" />
        <add key="SomeAnotherKey" value="2" />
    <AnotherSection/>
</configuration>

Even with such a small example, the entire configuration structure becomes clear, as it’s now obvious that the Count parameter is not related to the AnotherKey and SomeAnotherKey parameters. This is very relevant for utility tools, which typically have a large number of diverse parameters. Additionally, now the code doesn’t have any static classes for configuration. This object can simply be placed in DI and injected wherever needed. For testing, it’s enough to create an instance of the object and define the properties required for the test. Profit.

The library also offers a wide range of features. For example:

  • Configuration Providers - create your own configuration files and customize them.
  • Configuration Property Encryption - ability to work with encrypted sections.
  • Complex Type Serialization - ability to pass complex types in a single string, for example, by using the string "Ivan, Ivanov" to initialize an object with two fields: FirstName and LastName (more details here).
  • IList Types - ability to add data as a list.
  • Writable config - ability to save the modified configuration back to the file.

More details about the library’s features can be found on GitHub and on the West Wind Toolkit for .NET documentation site.