Конфигурация в ASP.NET Core. Создание собственного провайдера конфигурации ASP.NET Core

По умолчанию ASP.NET Core предоставляет довольно большой перечень поставщиков конфигурации приложения — для работы с аргументами командной строки, переменными среды, файлами json, xml, ini и т.д. Этого перечня вполне достаточно, чтобы покрыть максимум потребностей разработчиков. Однако, можно столкнуться с ситуацией, когда и этого богатого функционала будет недостаточно для работы и потребуется какое-то нестандартное решение для получения конфигурации.

Пример нестандартного конфигурационного файла

Рассмотрим пример, когда файл, содержащий конфигурацию приложения имеет нестандартный формат. Это реально используемый файл конфигурации в специальном программном обеспечении, которое широко используется специалистами в Европе.

CO STARTING                                                                                                                         
   TITLEONE A Simple Example Problem
   MODELOPT  CONC   FLAT
   AVERTIME  1  3  8  24  PERIOD                                                                                                          
   POLLUTID  SO2                                                                                                                    
   RUNORNOT  RUN
   ERRORFIL  ERRORS.OUT
CO FINISHED

Здесь название секции состоит всего из двух символов (CO), начало секции обозначается строкой [имя_секции] STARTING, а окончание — [имя_секции] FINISHED. Очевидно, что, если мы захотим использовать такие файлы конфигурации в приложении ASP.NET Core, то нам потребуется создание собственного провайдера конфигурации. Давайте попробуем это сделать.

Создание собственного провайдера конфигурации

Для создания конфигурации нам потребуется разработать три компонента:

  • Класс-наследник ConfigurationProvider — сам провайдер конфигурации
  • Класс, реализующий интерфейс IConfigurationSource  — определяет источник конфигурации,
  • класс, который добавляет метод расширения к объекту IConfiguration

Разработка провайдера конфигурации

Создадим класс-наследник от ConfigurationProvider, который будет разбирать файл конфигурации, представленный выше:

public class TxtConfigurationProvider : ConfigurationProvider
{
    public string FilePath { get; set; }

    public TxtConfigurationProvider(string filePath) 
    {
        FilePath = filePath;
    }   

    public override void Load()
    {
        if (File.Exists(FilePath)==false)
            throw new FileNotFoundException(FilePath);

        Data = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);

        using (StreamReader sr = new StreamReader(FilePath)) 
        {
            var dataStr = sr.ReadLine();
            string currentSection = string.Empty;
            while (dataStr != null) 
            {
                dataStr = dataStr.Trim();
                if (dataStr.Contains("STARTING"))//строка содержит начало секции
                    currentSection = dataStr.Substring(0, 2);
                else
                {
                    if (dataStr.Contains($"{currentSection} FINISHED")) //строка содержит окончание секции
                        currentSection = string.Empty;
                    else
                        if (currentSection == string.Empty) //строка содержит какую-то настройку из секции
                        throw new Exception("Ошибка: не обнаружено начало секции");
                    else
                    {
                        string[] option = dataStr.Split(new char[] {' '}, 2);//разбиваем строку на две: первая - имя настройки, вторая - значение
                        if (option.Length < 2)
                            throw new Exception($"Ошибка: в секции {currentSection} обнаружены ошибки записи настроек");

                        var key = $"{currentSection}:{option[0]}";
                        Data.Add(key, option[1]);
                    }    
                }
                dataStr = sr.ReadLine();
            }
        }
    }
}

У класса мы перегрузили метод Load, который, собственно и загружает конфигурацию из файла. В этом методе мы используем свойство класса ConfigurationProvider Data, которое должно представлять собой словарь Dictionary<string, string?> где ключ — это имя опции. Далее мы построчно читаем файл и записываем в Data полученные значения. Здесь стоит обратить внимание на строки:

var key = $"{currentSection}:{option[0]}"; 
Data.Add(key, option[1]);

Так как наша конфигурация имеет секции, то, в общем случае, ключ составляется следующим образом [название_секции]:[название_подсекции]:[название_опции]. В нашем случае файл конфигурации не может иметь подсекций, но каждая опция обязательно располагается в своей секции, поэтому ключ у нас записывается как [название_секции]:[название_опции].

Теперь, когда у нас есть провайдер конфигурации, мы можем создать источник конфигурации, то есть разработать класс, реализующий интерфейс IConfigurationSource

Разработка класса источника конфигурации

Источник конфигурации должен реализовать интерфейс IConfigurationSource, в частности — метод Build. В этот метод в качестве параметра передается строитель конфигурации (IConfigurationBuilder).

public class TxtConfigurationSource : IConfigurationSource
 {
     public string FilePath { get; }
     public TxtConfigurationSource(string filename)
     {
         FilePath = filename;
     }
     public IConfigurationProvider Build(IConfigurationBuilder builder)
     {
         string filePath = builder.GetFileProvider().GetFileInfo(FilePath).PhysicalPath;
         return new TxtConfigurationProvider(filePath);
     }
 }

Краткое название файла (его относительный путь) передается в класс источника через конструктор и хранится в свойстве FilePath. В методе Build мы получаем полный путь к файлу и передаем его в конструктор TxtConfigurationProvider. Теперь осталось написать метод расширения для IСonfiguration, который позволит добавлять текстовые файлы конфигурации в наше приложение.

Метод расширения для IConfiguration

Подобные методы расширения мы уже писали для других интерфейсов, поэтому создать очередной — не составит особого труда:

public static class TxtConfigurationExtensions
{
    public static IConfigurationBuilder AddTxtFile(this IConfigurationBuilder builder, string path)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }
        if (string.IsNullOrEmpty(path))
        {
            throw new ArgumentException("Путь к файлу не указан");
        }
        var source = new TxtConfigurationSource(path);
        builder.Add(source);
        return builder;
    }
}

Этот класс определяет для объекта IConfigurationBuilder метод расширения AddTxtFile(), в котором создается источник конфигурации TxtConfigurationSource, который затем добавляется к строителю конфигурации. Протестируем полученное решение.

Тестирование собственного провайдера конфигурации

Создадим текстовый файл с именем options.txt и добавим в него конфигурацию, как было показано выше:

CO STARTING                                                                                                                         
   TITLEONE A Simple Example Problem for the AERMOD Model with PRIME
   MODELOPT  CONC   FLAT
   AVERTIME  1  3  8  24  PERIOD                                                                                                          
   POLLUTID  SO2                                                                                                                    
   RUNORNOT  RUN
   EVENTFIL  aertest_evt.inp
   ERRORFIL  ERRORS.OUT
CO FINISHED

Теперь, спроецируем секцию OP на класс C#, как мы это делали ранее. Класс для хранения опций:

public class TxtOptions
{
    public string? Titleone { get; set; }
    public string? ModelOpt { get; set; }
    public string? Avertime { get; set; }
    public string? PollutId { get; set; }
    public string? RunOrNot { get; set; }
    public string? EventFil { get; set; }
    public string? ErrorFil { get; set; }
}

Код метода Main

public static void Main(string[] args)
{
    var builder = WebApplication.CreateBuilder(args);

    builder.Configuration.AddTxtFile("options.txt");
    
    var app = builder.Build();

    var section = app.Configuration.GetSection("co").Get<TxtOptions>();

    app.MapGet("/", () => $"{section.Titleone} - {section.Avertime}");

    app.Run();
}

Здесь мы добавили новую конфигурацию:

builder.Configuration.AddTxtFile("options.txt");

спроецировали секцию OP на класс TxtOptions

var section = app.Configuration.GetSection("co").Get<TxtOptions>();

и добавили новую конечную точку в которой перечислили несколько настроек:

app.MapGet("/", () => $"{section.Titleone} - {section.Avertime}");

Результат

Итого

Создание собственного провайдера конфигурации ASP.NET Core позволяет добавлять в приложение конфигурацию в любом формате. Для создания конфигурации нам потребовалось разработать три компонента: класс-наследник ConfigurationProvider, класс, реализующий интерфейс IConfigurationSourceи класс, который добавляет метод расширения к объекту IConfiguration.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии