Как стать автором
Обновить

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

Вы как-то лихо смешиваете ASP.NET Core и .NET Core в статье.

На самом деле получается довольно интересно.
Nuget пакеты с инфраструктурой конфигурации ни как не зависят от ASP.NET Core и таргетятся на .NETStandard 2.0. Таким образом их можно использовать не только в ASP.NET Core приложениях, но и в любых .NET Core и .NET Framework приложениях.
Слегка смущает лишь то, что весь код лежит в репозитории ASP.NET Core.
Вы как-то лихо смешиваете ASP.NET Core и .NET Core в статье.


Действительно лихо. Причина в том, что как вы уже писали пакет Microsoft.Extensions не зависит от ASP.NET Core. Но проблемы, которые я описывал касаются использования конфигурации в ASP.NET Core. В приложениях .NET Core и .NET Framework они могут и не возникнуть, потому что у нас нет волшебного метода CreateDefaultBuilder, который скрывает за кулисами всю рутинную работу.
Данная статья затрагивает лишь основы. Помимо основ нам доступны IOptions, сценарии пост-конфигурации, валидация настроек и многое другое. Но это уже другая история.

Самая интресная история.
Я работаю над продолжением этой истории. Если у вас есть вопросы по расширенным сценариям конфигурации, напишите, я попробую их разобрать.
Да просто напишите, как правильно делать Default опции, переключать конфиги в зависимости от ASPNETCORE_ENVIRONMENT, как управляеть переменными окружения и конфигами в тестах. про --launch-profile тоже.
В общем, про IRL-использование и хорошии практики, а не пересказ docs.microsoft.com/ru-ru/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.2
Мой вопрос напрямую не относится к теме статьи но все же.
Может быть вы знаете по какой причине разработчики ASP.NET Core ввели конфигурацию на основе переменных окружения? Насколько я знаю такой тип источника конфигурации не пользовался популярностью в .NET Framework? Вполне возможно что я пропустил что-то важное или же очевидное но никак не могу понять этот момент
К сожалению не могу ответить за разработчиков ASP.NET Core. Мне помнится в .NET Framework не было удобных инструментов для работы с переменными окружения. Или писать свою реализацию сервиса для получения настроек из переменной окружения. Или делать наследника ConfigurationBuilder, и делать чтение переменных там. Поправьте если ошибаюсь.
Насколько я помню есть ConfigurationManager, который так же доступен и в .NET Core
Доступен-доступен, доставить System.Configuration.ConfigurationManager, именно так и называется пакет, если память не подводит и вуаля, там единственный момент — в случае ASP Core он почитывает все из app.config (т.е. его производной), а не из web.config
Например, если вы собрали контейнер с каким-нибудь сервисом, а потом вам нужно этот контейнер запустить для тестов и подсунуть ему в качестве строки подключения фейковую БД для тестов, которую вы тут же рядом подняли в соседнем контейнере. Через переменные окружения это можно легко переопределить. У нас в связке с gitlab это многие используют.

Опять же передавать всякие секреты в различных окружениях тоже можно.
Не могу быть уверен, что послужило причиной, конечно, но в том же Kubernetes есть стандартные механизмы прокидывания настроек — config maps и secret references — доступ к которым можно получать в т.ч. через переменные окружения. Т.е. подход распространенный в облачном мире, решили упростить жизнь разработчикам.

Жалко только обязательных параметров без извращение с ioption не сделать никак…

И это то, что я сказал во второй части предложения.

Как по мне, так использование опций с DataAnnotation-атрибутами это самое лучшее решение. За некоторыми нюансами, что ошибка валидации будет происходит где-то в рантайме, только при запросе опций через DI. Но это обещают исправить в .NET Core 3.0


Если нет желания использовать опции, можно реализовать что-то типа IStartupFilter и там проверять конфигурацию. Но мне кажется это то еще извращение.

Я иногда вообще не хочу использовать DI. Не говоря про то, что у меня консольное приложение, без MVC. Зачем мне туда тащить эти IOption непонятно.


В итоге я отказался от всей этой конфигурации вообще и просто паршу JSON руками:


public class Config
{
    private readonly JObject _json;

    public Config(string file)
    {
        _json = JObject.Parse(File.ReadAllText(file));
    }

    public T GetSection<T>(string sectionName = null) where T : class
    {
        if (sectionName == null)
        {
            sectionName = typeof(T).Name;
        }
        return _json[sectionName]?.ToObject<T>() ??
               throw new InvalidOperationException($"Cannot find section {sectionName}");
    }
}

public class EthereumBaseSettings
{
    [JsonProperty(Required = Required.Always)]
    public string AccountAddress { get; }

    [JsonProperty(Required = Required.Always)]
    public string PrivateKey { get; }

    [JsonProperty(Required = Required.Always)]
    public string AccountPassword { get; }

    [JsonProperty(Required = Required.Always)]
    public string RootContractAddress { get; }

    [JsonProperty(Required = Required.Always)]
    public string ParityConnectionString { get; }

    public EthereumBaseSettings(string accountAddress,
                                string privateKey,
                                string accountPassword,
                                string rootContractAddress,
                                string parityConnectionString)
    {
        AccountAddress = accountAddress;
        PrivateKey = privateKey;
        AccountPassword = accountPassword;
        RootContractAddress = rootContractAddress;
        ParityConnectionString = parityConnectionString;
    }
}

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

Если в разных источниках конфигурации присутствуют одинаковые ключи (сравнение идет без учета регистра), то используется значение, которое было добавлено последним.

Это не всегда так. Для массивов будет просто добавление элемента в массив.

Да, вы отчасти правы. Для массивов, элементы с одинаковыми индексами будут заменены, с уникальными индексами будут добавлены. Добавлю в статью.

На сколько помню ничего не заменяется. Просто добавляется. Например в случае массива в appsettings.json и appsettings.Development.json

Это легко проверить.
К примеру при чтении массива из appsettings.json получим такой плоский вид:


array:0=valueA
array:1=value

При чтении из appsettings.Development.json:


array:0=valueB

В итоге в конфигурации будет:


array:0=valueB
array:1=value
В зависимости от переменной окружения выбирается тот или иной источник конфигурации.


Получается, что при сборке бинарников приложения там будут файлы конфигураций для всех environment и нужно как-то вручную удалять лишние?

Есть ли способы комбинирования настроек через переменные окружения и через файлы?
Например чтобы при запуске локально настройки брались из файла, а при запуске в тестовом окружении их переменных окружения?
Получается, что при сборке бинарников приложения там будут файлы конфигураций для всех environment и нужно как-то вручную удалять лишние?

Да, действительно при публикации приложения, все файлы .json попадают в выходную папку, не смотря на то, что в их свойствах стоит опция Do not copy. Чтобы решить эту проблему можно добавить в .csproj MSBuild conditional constructs с , но это не выглядит прямым решением проблемы.

Есть ли способы комбинирования настроек через переменные окружения и через файлы?

Есть. Вспомним пример:


{
 "Settings": {
  "Key": "I am options"
 }
}

Будет приведен к плоскому виду:


Settings:Key = I am options

Если создать переменную окружения с ключом Settings__Key, она заменит настройку из файла. Подробнее здесь.

Я для проектов .NET Framework и .NET Core использую библиотеку SlowCheetah. Она есть и в нугете и можно поставить как экстеншн в студию. Экстеншн позволяет в один клик сделать пачку конфигов автоматом под все имеющиеся профили паблиша, а библиотека сама делает трансформацию при паблише. Из настроек — добавить по мануалу в файл проекта несколько строк. Для деплоя используем дженкинс и очень удобно, когда в один клик все само трансформируется.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.