- PVSM.RU - https://www.pvsm.ru -

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

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

Установка

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

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

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

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

<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

Источник [3]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/net/250848

Ссылки в тексте:

[1] NuGet: https://www.nuget.org/packages/DotNetCraft.ConfigurationParser

[2] GitHub: https://github.com/DotNetCraft/ConfigurationParser

[3] Источник: https://habrahabr.ru/post/324868/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox