Думаю многим, кто работал с .Net framework знакома ситуация, когда надо читать конфигурационный файл App.config, и то, что это не особо приятное занятие. Всегда приходится городить кучу статических классов, содержащих по сути обращение к ConfigurationManager.AppSettings. Либо писать сложные и непонятные кастомные секции файлов конфигурации, вписывать каждую секцию в раздел configSection со ссылкой на класс секции и его ассембли, что тоже не очень хорошо. Если сравнивать с dotnet core, который умеет десериализовать json и мапить его на объект, то ситуация с обычным .net framework выглядит удручающе.

Зачастую все сводится к следующему коду:

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();
        }
    }
}

И соответствующему конфигурационному файлу:

<?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>

Это несомненно простой и действенный способ, при условии, что у вас всего 5-10 параметров. Также такой подход порождает статические классы, а это значит, что протестировать такой код будет уже намного сложнее, что тоже плохо. Кончено, как вариант, всегда можно просто использовать сторонний файл любого формата, но, на мой взгляд, это зачастую избыточно.

К счастью, есть выход и возможность использовать стандартные файлы конфигурации на подобии работы с конфирмацией в dotnet core. Имя этой спасительной библиотеке West Wind Application Configuration.

Приведенный выше код может быть переписан следующим образом:

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();
       }
    }
}

А файл конфигурации вот так:

<?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>

Даже на таком маленьком примере становится понятна вся структура конфигурации, так как теперь стало понятно, что параметр Count никак не взаимосвязан с параметрами AnotherKey и SomeAnotherKey. Это очень актуально для служебных тулз, которые, как правило, имеют большое количество разнообразных параметров. Также теперь код не имеет ни одного статического класса для конфигурации. Такой объект можно просто положить в DI и инжектить по мере надобности в любом месте. При тестировании достаточно просто создать инстанс объекта и определить его свойства необходимые для теста. Profit.

Также библиотека имеет большое количество возможностей. Например:

  • Configuration Providers - создавать свои файлы конфирмации и настраивать их.
  • Configuration Property Encryption - возможность работать с зашифрованными секциями
  • Complex Type Serialization - возможность передавать одной строкой комплексные типы, например, с помощью строки "Ivan, Ivanov" инициировать объект с 2 полями FirstName and LastName (подробнее тут).
  • IList Types - возможность добавлять данные списком.
  • Writable config - возможность сохранить измененную конфигурацию в обратно файл.

Подробнее с возможностями библиотеки можно познакомиться на github и сайте документации West Wind Toolkit for .NET.