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

В данной статье мне бы хотелось рассмотреть проблему загрузки настроек из конфигурационных файлов. Как правило, разработчики используют тяжеловесное и сложное 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 делает работу по чтению настроек из конфигурационного файла очень удобной и сохраняет кучу времени. При этом она легко расширяется благодаря подключению новых стратегий.

Хотелось бы упомянуть еще об одной возможности данной библиотеки. Это аттрибут PropertyMappingAttribute. Его нужно использовать в случае когда название элемента в конфигурационном файле не совпадает с названием свойства.
Например, у нас есть секция Email, где записывается текущий почтовый адрес пользователя.
<Email>clien1@gmail.com</Email>

А вот в класс для хранения данного значения у нас предусмотрено свойство UserEmail. Чтобы настройки правильно считались необходимо поменить данное свойство аттрибутом PropertyMappingAttribute.
[PropertyMapping("Email")]
public string UserEmail { get; set; }

Таким образом названия элементов в конфигурационном файле необязательно должны совпадать с именами свойств.

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

Подробнее
Реклама

Комментарии 44

    0
    Тупой вопрос от js-кодера: а почему плохо просто построить XML DOM и читать всё оттуда?
      0
      Приветствую

      В мире .NET разработчики привыкли работать с объектами. Поэтому практически в любом приложении можно встретить строчки

      AppSettingsSection appSettings  = (AppSettingsSection)config.GetSection("appSettings");
      string connectionString = appSettings.Settings["SqlConnectionString"].Value;
      MyAppSettings myAppSettings = new MyAppSettings (connectionString);
      


      Используем myAppSettings.ConnectionString по своему усмотрению.

      Это очень простой пример, но даже в нем нам приходится собственноручно создавать объект с настройками и заполнять его. Библиотека все это сделает за нас.

      Все усложняется, когда появляются настройки со многими вложенностями. Например, в моем текущем проекте у нас в зависимости от страны — свои настройки, в которых прописываются подключения к различные сторонним сервисам, строки подключения к БД и тд. В результате чего для загрузки конфигурационного файла пришлось бы писать очень много кода, а так — создал классы и загрузил все одной строчкой.
        +1
        Дополню ответ DotNetCraft.
        В случае прямой работы придется вводить название секций и атрибутов в виде строк.
        А это прямой путь к опечаткам и головной боли при переименовании.
          +1
          А каким образом приведённый здесь подход избавляет от опечаток и боли? Всё равно где-то придётся прописывать имя тега, откуда берётся настройка, телепатию в дотнет вроде ещё не завезли. И если что-то переименуется, всё равно придётся что-то в связи с этим менять. Разве нет?
            0
            В примере автора имя тега совпадает с именем поля, если изменится, то просто дописываем атрибут, а библиотека за нас смаппит. Даже для загрузки секции можно написать обертку, чтобы не вводить её имя вручную.
            У вас же при изменении атрибута или секции придется изменять все места, где она используется.
              0
              лучше ошибиться в одном месте, чем в нескольких. разве нет?
                0
                Дополню Deosis

                Если в конфигурационном файле будет жлемент, а в классах его не будет, то система сгенерит Exception. Таким образом происходит проверка конфига и классов.
          +7
          Ещё наверно тупее вопрос — а почему просто не использовать json? Пишешь нужные тебе классы, автоматически сериализуешь\десериализуешь без всяких монструозных инструментов. Читабельно, быстро, просто, удобно.
            +1
            Приветствую

            Listrigon правильно ответил про стандартную систему конфигурационных файлов .NET. От себу лишь слегка добавлю, что в .NET для хранения конфигурации используется App.Config, в котором и записывается все настройки. Формат хранения данных — XML.

            Более подробно можно прочитать здесь:
            1. https://msdn.microsoft.com/en-us/library/1xtk877y.aspx
            2. https://msdn.microsoft.com/en-us/library/ms254494(v=vs.110).aspx
              +1
              Скорее всего потому, что все это дело является частью стандартной системы конфигурационных файлов .NET.
              Чтобы не делать отдельного файла под свои настройки нужно быть частью структуры этого файла, который пользуется и другими библиотеками. Хотя да, с JSON было бы все проще
              +1
              А в чем отличие от Custom Configuration Sections? Только то, что POCO без атрибутов? Я не вижу в System.Configuration особых сложностей и тяжестей.
                0
                Приветствую
                На мой взгляд прелесть в том, что кода становится меньше.

                Для примера возьмем секцию из статьи по вашей ссылке

                <StartupFolders>
                   <Folders>
                     <Folder folderType = "A" path="c:\foo" />
                     <Folder folderType = "B" path="C:\foo1" />
                   </Folders>
                 </StartupFolders>
                


                Создадим классы

                class StartupFolders
                {
                    public Folders Folders { get; set; }
                }
                
                class Folders
                {
                    public List<Folders> Folders { get; set; }
                }
                
                class Folder
                {
                    public string FolderType { get; set; }
                    public string Path { get; set; }
                }
                


                Вызовем загрузчик
                StartupFolders startupFolders= (dynamic)ConfigurationManager.GetSection("StartupFolders");
                


                Ну и как бы все :) Мы загрузили все настройки.

                Обратите внимание, что я нигде не использовал ключи а-ля «Folders», что, с моей точки хрения, уменьшает ошибку при копипасте.

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

                  … а теперь, значит, вы хотите переименовать свойство Folders.Folders в Folders.NewFolders, но не хотите трогать конфигурационные файлы (т.е., в них элемент так и должен остаться Folders). И как это сделать?

                    +1
                    Если я правильно понял вопрос, то

                    [PropertyMapping("Folders ")]
                    public List<Folders> NewFolders { get; set; }
                    


                    Это означает, что в конфигу мы ищем Folders и грузим данные в NewFolders.

                    Хотя, если честно, то я почти не сталкивался с ситуациями, когда надо было поменять имя… Вот структуру — это да, частое явление. То мы храним это в Листе, то давайте сделаем словарь…

                    З.Ы. Забыл упомянуть об этом аттрибуте в статье.
                +2
                вместо
                            string[] items = input.Split(';');
                            List<string> result = new List<string>();
                
                            result.AddRange(items);
                
                            return result;
                


                может лучше
                return input.Split(';').ToList();
                
                  +1
                  Приветствую

                  Да, соглашусь, что можно input.Split(';').ToList();

                  Однако, я хотел акцентировать внимание на то, что в этом методе можно написать любую логику. Например, в текущем примере мы можем добавить проверку правильность email'ов и сгенерить Exception если что-то не так.
                • НЛО прилетело и опубликовало эту надпись здесь
                    +2
                    Приветствую

                    Да, соглашусь, что можно input.Split(';').ToList();

                    Однако, я хотел акцентрировать внимание на то, что в этом методе можно написать любую логику. Например, в текущем примере мы можем добавить проверку правильность email'ов и сгенерить Exception если что-то не так.
                    +2
                    В .NET настройки делятся на настройки приложения и пользовательские. (вторые доступны для изменения, первые только для чтения [если не править файл вручную])
                    Библиотека как-либо обрабатывает эти различия?
                    Например, пользователь переопределил несколько настроек и хочет их сохранить.
                    Что нужно использовать вместо Configuration.Save()?
                      0
                      Основное предназначение этой библиотеки — чтение настроек приложения, поэтому методы сохранения не предусмотрены.

                      Если честно, то я не стрононник что-либо менять в конфиге приложения в рантайме. Если что-то и надо заменить, то, используя, например, TeamCity+Octopus, создаем релиз и деплоим его с измененными настройками. Это относится к настройкам приложения.

                      Если же речь идет о пользовательских, то, как правило эти данные хранятся в БД и выходят за рамки данной либы.
                        0
                        я не стрононник что-либо менять в конфиге приложения в рантайме.

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

                        ЗЫ. Атрибут «configSource» продолжает нормально поддерживаться?
                          0
                          И, заодно, я так понимаю, у вас нет поддержки стандартного паттерна коллекций в web/app.config?

                          <items>
                              <clear />
                              <add name="Item 1" />
                              <add name="Item 2" />
                          </items>
                          
                            0
                            Под «рантаймом» я понимаю, когда программа запущена и она сама меняет ее конфиг. Вот я именно против этого. А вот когда в процессе развертывания кто-то меняет конфиг, так это я «за». Например, мы создаем релизный конфиг и там указываем заменить connectionString на реальзое значение в зависимости от среды развертывания. Что-то мне подсказывает, что в этом вопросе мы по одну сторону баррикады.

                            Что касается <clear/> и configSource… Я с этимт «параметрами» сталкивался очень давно и опыт был негативным, так что я стараюсь их избегать. Более того, это лично мое мнение, задвать в конфигурации, то что конфигурация в таком-то файле как-то странновато. Смысл данного действия для меня представляется только в том, чтобы разделить Debug от Release, ну, или разные конфиги для разных клиентов. Все это легко делается путем создания конфигурационного файла с разметкой и, в момент создания билда, автоматически подставляем нужные параметры.
                              0
                              «clear/add/remove» упрощает создание иерархических конфигов с настройками по-умолчанию: есть некий корневой конфиг с дефолтными элементами коллекции, и в своём под-конфиге можно переиспользовать их, можно добавить свои, можно убрать некоторые (или все) дефолтные. Без этого придётся каждый раз указывать все необходимые элементы. С одной стороны, это вроде как хорошо — все элементы явно присутствуют, с другой — конфиг может быть сильно перегружен вспомогательными элементами, появляется соблазн копипастить и т.п.

                              configSource — это как "#include" или «using» или «import» — полезен тем, способствует модуляризации: можно разбить огромный неудобоваримый конфиг с кучей настроек «обо всём» на несколько более сфокусированных конфигов. Например, ту часть, что настраивается девелопером и далее не предполагается перенастраивать (всякие там assemblyBindings и проч), хранить в основном модуле, а части, которые нужно настраивать при деплое — в своих модулях. Кроме того, можно создать несколько под-конфигов на разные случаи (скажем, с разными тонкими настройками производительности), и подключать/переключать их одним движением, не переписывая основной конфиг, и не дублируя его.

                              В чём был ваш негативный опыт? У меня как раз негативный опыт от одного супер-конфига, в основном из-за его вырвиглазности и из-за копипасты, распространяющейся как рак.
                                0
                                Забыл добавить: при разбиении конфига на части и иерархии/композиции появляется возможность тонко настраивать безопасность, назначая разные права на разные секции.
                                  0
                                  Если я правильно понял, то все эти приемы направлены чтобы превратить мегагромадный конфиг во что-то удобоваримое…

                                  Опыт как раз в том, что огромный конфиг пытались разбить на подконфиги и тем самым только усугубив проблему, вместо того, чтобы разобраться в чем причина. А проблема-то была в том, что система взяла на себя слишком много (получился этакий монолит-гигант) и плюс не было нормального деплоймента (конфиги ручками менялись для debug, test, sandbox и пр). Зато потом, когда все-таки разбили этот кусок гиганта на сервисы (мини или микро — это пожеланию), то монстрообразный конфиг пропал. Вместо него появились несколько мелких конфигов и для каждой среды свой конфиг.
                                  Пример:
                                  • app.config — это конфигурация у разработкича
                                  • app.Release.config — это для всех остальных (тест, релиз и пр)

                                  При деплое меням переменные в указанном конфиге. Например, при деплое тестировщикам имеем конфиг вида
                                  <DatabaseSettings  ConnectionString=#{CurrentConnectionString}/>
                                  

                                  В Octopus'e задаем значение для CurrentConnectionString
                                  • CurrentConnectionString = TestDatabase для тестирования
                                  • CurrentConnectionString = SandBox для предрелиза
                                  • CurrentConnectionString = Release для релиза.

                                  Нажимаем на кнопку и деплоим куда надо. Переменная заменится на нужное значение автоматом.
                                    0
                                    Для меня «мегагаромадный» начинается уже с момента, когда кастомные настройки составляют менее 50% от веса всего конфига. Т.е. если конфиг занимает целый экран, и в нём всего одна настройка — коннект к базе данных, — то это кандидат на декомпозицию.

                                    Возможно это связано с тем, что деплоймент у нас действительно отличается от вашего — мы не деплоим сервисы у себя, мы отдаём их заказчикам. И нам нужно обеспечить, чтобы наши конфиги были простыми и понятными, даже если у заказчика нет полноценного деплоймента и он выкатывает сервисы вручную, настраивая конфиги в Блокноте. К сожалению, почти все статьи заточены на единственный сценарий деплоймента сервисов у себя дома, где можно поставить сколь угодно продвинутый деплоймент.
                                      0
                                      Натройка конфига в блокноте — это шедеврально. Посоветуйте им хотя Nodepad++. А вообще, предложите им свои услуги по настройке.

                                      Мы разворачиваем на разных компах, что-то у нас, что-то у заказчика, но всегда стараемся минимизировать ручное редактирование конфига. Плюс, конфигурационный файл должен содержать только ту информацию, которая нужна данному конкретному микросервису. Т.о. образом конфиг не разбухает, до неимоверных размеров.
                                        0
                                        Натройка конфига в блокноте — это шедеврально.

                                        «Когда б вы знали, из какого сора...»

                                        А вообще, предложите им свои услуги по настройке.

                                        У нас есть solution team, но его тоже нужно содержать, обучать и, что хуже, постоянно переобучать, иначе они сами оказываются тормозом, а не источником дохода. Часто проще сделать простые конфиги и внятную документацию, понятную даже дураку.

                                        всегда стараемся минимизировать ручное редактирование конфига.

                                        Мы тоже, но без деплой-сервера (а мы не можем гарантировать его налиичие у каждого из кучи мелких кастомеров) это означает написание собственного конфигурационного визарда для каждого микросервиса. Это дополнительные затраты на девелопмент и тестирование, без особого финансового выхлопа. Мы предпочитаем, чтобы кастомер сам заботился об автоматизации, и чтобы даже при ручном деплое он не сильно отвлекал нас вопросами, которых можно было бы избежать, сделав всё просто и хорошо это документировав.

                                        конфигурационный файл должен содержать только ту информацию, которая нужна данному конкретному микросервису.

                                        И я о том же. Но даже у самого микро- из микросервисов есть два типа конфигурационных данных: те, что диктуются выбранным фреймворком/библиотеками, настроены девелопером и не предполагаются к изменению кастомером (скажем, многочисленные настройки WCF со всем ворохом сервисов, байндингов, behaviours и т.п., или настройки dependency injector-а, или настройки доступа к разным путям), и те, которые предполагаются быть настроенными кастомером (в простейшем случае, секция «appSettings»). Причём настроек первого типа может быть огромное количество, и зависит оно не от девелопера. Девелопер рад бы иметь малеький конфигурационный файл с только теми настройками, которые он сам создал, но без разбиения конфига на части это практически нереально.
                      +1
                      Никогда не понимал возни с Settings (или как там этот класс зовут) и вообще использование словарей для хранения/доступа к настройкам…

                      Пишется свой класс, расставляются аттрибуты, сериализуется/десериализуется чем вам больше нравится (json.net, XmlSerializer, ...) в куда вам необходимо (в MemoryStream -> byte[] -> db или же в FileStream)… Если конфигурация одна — синглтон, несколько — доступ через менеджер (Config.Active, Config.Load, etc.).

                      Все вышеперечисленное занимает буквально десяток строк (если не реализовывать что-то слишком навороченное, к примеру версионинг).

                      Имхо нет смысла в надстройке над чем-то дефолтовым и кривым. Плюс я не очень понял чем же лучше
                      SmtpSettings smtpSettings = (dynamic)ConfigurationManager.GetSection(«SmtpSettings»)
                      особенно доставляет dynamic и (на внимательность) «StmpSettings».
                        0
                        Как оказалось, некоторые любят app.config.
                        Я бы ещё понял web.config, но app.config мне категорически не нравится. Пользователя туда не пошлешь, там могут быть и программные настройки.
                          +1
                          Это утилита работает также с web.config…

                          Пользователя и не надо туда слать. Для этого существуют другие места. App.Config предназначен для хранения настроек приложения и очень хорошо с этим справлется. Добавляем к нему build-сервер (например, TeamCity) и Octopus (мне он нравится) и мы получаем развертывание приложения в один клик/чекин для разных сред (Dev, Test, Sandbox, Release). Поэтому я не понимаю почему он вам так не нравится…
                          0
                          Как правило это работает на маленький проекта без автоматических билдов и развертываний или в самом начале разработки, когда приложение опять-таки маленькое. Когда приложение разрастается до несколько десятков сервисов, да еще и с подключением к сотне других сторонних программ… Вот тут и начинается, что надо бы такую настроечку, а вот тут при чтении конфигурации обязательно проверить, что переменные в Octopuse имеют правильные значения и получается, что изначальные 10 строчек кода в разных проектах превращабтся в 20 в одном, 50 в другом, 10 в третьем, причем от исходных может вообще ничего не остаться.
                          0
                          В .NET Core из коробки идет хорошее решение, которое и вложенность любую поддерживает, и принцип разделения ответственности, и встроенный в фреймворк DI container, и вообще. Вот тут можно глянуть, начиная с раздела «Using Options and configuration objects».
                            –1
                            Неплохая утилита, но есть как минимум два «но».
                            1. Читает json.
                            2. Нужен .NET Core

                            Мне больше всего не нравится то, что конфигураци хранится в json файлах… Сейчас попробую объяснить почему.

                            При создание проекта в .NET автоматически будет создан app.config, в который студия поместит какие-то свои данные. Затем, например, мы добавим WCF сервис и в том же файле увидим его параметры. При этом, давайте предположим, что мы используем самописный ORM и поэтому строку подключения можем хранить где угодно и мы выбрали json… Как-то выглядит, что настройки расползлись по двум файлам вместо одного…
                              0
                              Не, .NET Core же как раз в json переполз настройками, в том числе и теми, что «автоматически будет создан...»
                              В последней версии, правда, вроде обратно переползли, я её еще не ковырял.

                              Это что касаемо пункта 1 вашего.
                              Что касаемо пункта 2 — ну это уже надо как данность воспринимать. Вероятнее всего, что в ближайшие пару лет все дружной толпой будем в сторону .NET Core двигаться.
                                +1
                                Пока это будущее наступит пройдет время в течении которого надо выпускать продукты, да еще и поддерживать и развивать старые…

                                Но, все равно, спасибо за комментарий и ссылку на статью.
                                0
                                .NET Core поддерживает из коробки чтение JSON и XML конфигураций. По умолчанию просто выбирается JSON.
                            • НЛО прилетело и опубликовало эту надпись здесь
                                0
                                Интересный подход. По-сути, вы написали свою «библиотеку», подобной ConfigurationParser, но на Т4.

                                Кстати, а если в Т4 появляется ошибка, то как тестировать? Несколько раз посоздавали разные конфиги и на этом все?
                                На и в догонку, а как быть если адреса почты написаны через точку с запятой, а в системе они должны быть с листе, да еще и проверены с помощью RegEx?

                                И на последок, если это не коммерческая тайна, то можно взглянуть на код?
                                • НЛО прилетело и опубликовало эту надпись здесь
                                    0
                                    Спасибо за пример. Попробую поиграться…

                                    А тем временем позвольте немного критики.

                                    Если я правильно понял, то вот этот длинный код создает класс с одной переменной и распарсивает значение из конфига.На мой взгляд как-то не рационально.

                                    Также, если мы захотим добавить еще логики, то мы должны менять tt? Например, повторные адреса игнорировать. Можно, конечно, написать partial class и там реализовать метод со всякими проверками, а в tt его только дергать… По-моему как-то слишком сложно. Учитывая, что конфиг это та часть программы, которая очень редко меняется.

                                    З.Ы. Еще раз спасибо за пример — буду разбираться.
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                        0
                                        А если создается что-то новое, более-менее часто, то как скопировав tt в новый проект, у вас уже все сгенерированно.

                                        Новый проект — новый конфиг. А учитывая движение в сторону микросервисов, то получается, что и конфиги почти пыстые. Максимум 5-7 микросервисов (это мой опыт) и всякие частные настройки для конкретного сервиса. Т.о. повторное использование tt тут не подходит. Да и любое повторное использование кода не подходит. Конфиг почти каждый раз пишется с 0.

                                        Надо сгенерировать mock config для тестов — поменяли пару строчек в шаблоне и он вам тестовые классы сделал.

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

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

                                        Ну и самое главное, это не единичный шаблон, а часть общей системы которая позволяет автоматизировать рутинные части.

                                        Вот после этого я спорить не буду ибо согласен. Если есть смысл и возможность, то надо генерить.

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое