Распарсивание конфигурационных файлов без проблем

в 10:43, , рубрики: .net, api, config, configuration, parser, read config, XML

В данной статье мне бы хотелось рассмотреть проблему загрузки настроек из конфигурационных файлов. Как правило, разработчики используют тяжеловесное и сложное API из пространства имен System.Configuration и считывают настройки шаг за шагом. В случае если в конфигурационном файле секция, которую надо считать, представляет из себя простую структуру (без вложенностей), то, в принципе, считывание не вызывает особых проблем. Однако, как только конфигурация усложняется и/или появляются вложенные подсекции, то распарсивание превращается в настоящую головную боль. Для простого и быстрого считывания настроек и загрузку их в память отлично подойдет библиотека ConfigurationParser, которая возьмет на себя все сложности работы с конфигурационными файлами.

Установка

Данная библиотека доступна для скачивания через NuGet. Или вы можете скачать исходники из GitHub.

Использование

Для демонстрации возможностей библиотеки давайте представим, что нашей программе требуются настройки для подключения к нескольким внешним сервисам, базам данных и наша система должна уметь отправлять письма администраторам системы. Мы решили, что данные настройки будут находится в трех секциях.

Конфигурация системы

<ExternalSystemSettings>
    <AuthenticationSettings>
      <Login>DotNetCraft</Login>
      <Token>qwerty</Token>
      <Urls>
        <Url>https://github.com/DotNetCraft/ConfigurationParser</Url>
        <Url>https://github.com/DotNetCraft/ConfigurationParser</Url>
      </Urls>      
    </AuthenticationSettings>
    <StaffSettings Token="{D0C148F7-83C0-41B0-8F18-B47CAB09AD99}" Url="https://github.com/DotNetCraft/ConfigurationParser"/>
  </ExternalSystemSettings>

  <DatabasesSettings>
    <MongoSettings ConnectionString="mongo.url" DatabaseName="DotNetCraft"/>
    <SqlSettings>
      <item key="TenantA">
        <value>
          <SqlSettings ConnectionString="sql.TenantA.com"/>
        </value>
      </item>
      <item>
        <key>TenantB</key>
        <value>
          <SqlSettings>
            <ConnectionString>sql.TenantB.com</ConnectionString>
          </SqlSettings>
        </value>
      </item>     
    </SqlSettings>
  </DatabasesSettings>

  <SmtpSettings Host="gmail.com" Sender="no-reply">
    <Recipients>clien1@gmail.com;clien2@gmail.com;clien3@gmail.com</Recipients>
  </SmtpSettings>

Следующим шагом необходимо создать классы, в которых мы будем хранить настройки системы.

Конфигурационные классы

    #region ExternalSystemSettings
    class ExternalSystemSettings
    {
        public AuthenticationServiceSettings AuthenticationSettings { get; set; }
        public StaffServiceSettings StaffSettings { get; set; }
    }

    class AuthenticationServiceSettings
    {
        public string Login { get; set; }
        public string Token { get; set; }
        public List<string> Urls { get; set; }
    }

    class StaffServiceSettings
    {
        public Guid Token { get; set; }
        public string Url { get; set; }
    }
    #endregion

    #region DatabasesSettings
    class DatabasesSettings
    {
        public MongoDatabaseSettings MongoSettings { get; set; }
        public Dictionary<string, SqlSettings> SqlSettings { get; set; }
    }

    class MongoDatabaseSettings
    {
        public string ConnectionString { get; set; }
        public string DatabaseName { get; set; }
    }

    class SqlSettings
    {
        public string ConnectionString { get; set; }
    }
    #endregion

    #region Smtp
    class SmtpSettings
    {
        public string Host { get; set; }
        public string Sender { get; set; }

        [CustomStrategy(typeof(SplitRecipientsCustomStrategy))]
        public List<string> Recipients { get; set; }
    }
    #endregion

В конфигурационном файле нам необходимо объявить, что мы будем использовать ConfigurationParser.

Заполнение configSections в App.Config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

  <configSections>
    <section name="ExternalSystemSettings" type="DotNetCraft.ConfigurationParser.SimpleConfigurationSectionHandler, DotNetCraft.ConfigurationParser" />
    <section name="DatabasesSettings" type="DotNetCraft.ConfigurationParser.SimpleConfigurationSectionHandler, DotNetCraft.ConfigurationParser" />
    <section name="SmtpSettings" type="DotNetCraft.ConfigurationParser.SimpleConfigurationSectionHandler, DotNetCraft.ConfigurationParser" />
  </configSections>
…
</configuration>

Теперь мы можем считать нашу конфигурацию легко и непринужденно:

ExternalSystemSettings externalSystemSettings = (dynamic)ConfigurationManager.GetSection("ExternalSystemSettings");
DatabasesSettings databasesSettings = (dynamic)ConfigurationManager.GetSection("DatabasesSettings");

После выполнения кода у нас будет создано 2 объекта, которые и будут содержать наши настройки. Как вы можете справедливо заметить, мы не считываем SmtpSettings. Это сделано для того, чтобы продемонстрировать дополнительные возможности утилиты.

В различных проектах вам может понадобиться записывать разнообразные настройки в конфигурационные файлы и иметь свою логику как с ними работать. Утилита спроектирована таким образом, что вы можете ее легко расширить путем добавления новой стратегии и покрыть все сценарии, которые вам нужны. Для примера, давайте рассмотрим секцию Recipients:

<Recipients>clien1@gmail.com;clien2@gmail.com;clien3@gmail.com</Recipients>

Эта секция содержит список email-ов, разделенных точкой с запятой. Согласно нашему ТЗ и классу мы должны записать каждый адрес как отдельный элемент в массиве. Для выполнения поставленной задачи нам надо создать класс SplitRecipientsCustomStrategy и реализовать интерфейс ICustomMappingStrategy

Посльзовательская стратегия

class SplitRecipientsCustomStrategy : ICustomMappingStrategy
    {
        #region Implementation of ICustomMappingStrategy

        public object Map(string input, Type itemType)
        {
            string[] items = input.Split(';');
            List<string> result = new List<string>();

            result.AddRange(items);

            return result;
        }

        public object Map(XmlNode xmlNode, Type itemType)
        {
            string input = xmlNode.InnerText;
            return Map(input, itemType);
        }

        #endregion
    }

Внутри нам необходимо реализовать алгоритм распарсивания значения из секции в соответствии с нашим заданием. Необходимо учитывать, что метод Map(string input, Type itemType) вызывается если значение прочитано из атрибута, а метод Map(XmlNode xmlNode, Type itemType) вызывается если значение прочитано из секции. Согласно конфигурационному файлу, значение будет прочитано из секции.

После этого нам необходимо пометить свойство Recipients атрибутом CustomStrategy в котором нужно указать, что за «стратегия» будет использоваться для данного поля:

[CustomStrategy(typeof(SplitRecipientsCustomStrategy))]
public List<string> Recipients { get; set; }

Вот и все, теперь мы можем считать настройки электронной почты.

SmtpSettings smtpSettings = (dynamic)ConfigurationManager.GetSection("SmtpSettings");

Как вы видите, ConfigurationParser делает работу по чтению настроек из конфигурационного файла очень удобной и сохраняет кучу времени. При этом она легко расширяется благодаря подключению новых стратегий.

Если у вас возникнут какие-либо вопросы или предложения, пожалуйста, пишите их в комментариях, я с радостью на них отвечу.

Автор: DotNetCraft

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js