Русский
Русский
English
Статистика
Реклама

Из песочницы IOptions и его друзья

Во время разработки часто возникает потребность для вынесения параметров в конфигурационные файлы. Да и вообще хранить разные конфигурационный константы в коде является признаком дурного тона. Один из вариантов хранения настроек использования конфигурационных файлов. .Net Core из коробки умеет работать с такими форматами как: json, ini, xml и другие. Так же есть возможность писать свои провайдеры конфигураций. (Кстати говоря за работу с конфигурациями отвечает сервис IConfiguration и IConfigurationProvider для доступа к конфигурациям определенного формата и для написания своих провайдеров)


image


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


На MSDN есть статья, которая должна раскрывать все вопросы. Но, как всегда, не все так просто.


IOptions


Does not support:
Reading of configuration data after the app has started.
Named options

Is registered as a Singleton and can be injected into any service lifetime.

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


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


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


IOptionsSnapshot


Is useful in scenarios where options should be recomputed on every request

Is registered as Scoped and therefore cannot be injected into a Singleton service.

Supports named options

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


MSDN нам говорит, что не может быть заинжекчен в Singletone на самом деле может (это прям тема для отдельного поста), но тогда и сам он начинает себя вести как Singletone.


IOptionsMonitor


Is used to retrieve options and manage options notifications for TOptions instances.

Is registered as a Singleton and can be injected into any service lifetime.

Supports:
Change notifications
Named options
Reloadable configuration
Selective options invalidation (IOptionsMonitorCache)

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


IOptionsMonitorCache интерфейс для построения обычного кэша на базе IOptionsMonitor.


Практика


Все тесты проводились на следующем окружении


sw_versProductName:    Mac OS XProductVersion: 10.15.5BuildVersion:   19F101dotnet --version3.1.301

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


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


В качестве примера будет простое Web API


 public class Program {     public static void Main(string[] args)     {         CreateHostBuilder(args).Build().Run();     }     public static IHostBuilder CreateHostBuilder(string[] args) =>         Host.CreateDefaultBuilder(args)             .ConfigureWebHostDefaults(webBuilder =>             {                 webBuilder.UseKestrel();                 webBuilder.UseStartup<Startup>();                 webBuilder.UseUrls("http://*:5010/");             })             .UseDefaultServiceProvider(options => options.ValidateScopes = false); }

Клиент, который будет к нему обращаться


 static async Task Main(string[] args) {     using var client = new HttpClient();     var prevResponse = String.Empty;     while (true)     {         var response = await client.GetStringAsync("http://localhost:5010/settings");         if (response != prevResponse) // пишем в консоль только, если настройки изменились         {             Console.WriteLine(response);             prevResponse = response;         }     } }

В Web API создаем 3 сервиса, который принимает все 3 варианта конфигураций в конструктор и возвращают текущее значение.


private readonly IOptions<TestGroupSettings> _testOptions;private readonly IOptionsSnapshot<TestGroupSettings> _testOptionsSnapshot;private readonly IOptionsMonitor<TestGroupSettings> _testOptionsMonitor;public ScopedService(IOptions<TestGroupSettings> testOptions, IOptionsSnapshot<TestGroupSettings> testOptionsSnapshot,    IOptionsMonitor<TestGroupSettings> testOptionsMonitor){    _testOptions = testOptions;    _testOptionsSnapshot = testOptionsSnapshot;    _testOptionsMonitor = testOptionsMonitor;}

Сервисы будут 3х скоупов: Singletone, Scoped и Transient.


public void ConfigureServices(IServiceCollection services){    services.Configure<TestGroupSettings>(Configuration.GetSection("TestGroup"));    services.AddSingleton<ISingletonService, SingletonService>();    services.AddScoped<IScopedService, ScopedService>();    services.AddTransient<ITransientService, TransientService>();    services.AddControllers();}

В процессе работы нашего Web Api изменяем значение TestGroup.Test файла appsettings.json


Имеем следующую картину:
Сразу после запуска


SingletonService IOptions value: 0SingletonService IOptionsSnapshot value: 0SingletonService IOptionsMonitor value: 0ScopedService IOptions value: 0ScopedService IOptionsSnapshot value: 0ScopedService IOptionsMonitor value: 0TransientService IOptions value: 0TransientService IOptionsSnapshot value: 0TransientService IOptionsMonitor value: 0

Изменяем нашу настройку и получаем интересную картину
Сразу после изменения


SingletonService IOptions value: 0SingletonService IOptionsSnapshot value: 0 // не измениласьSingletonService IOptionsMonitor value: 0 // не измениласьScopedService IOptions value: 0ScopedService IOptionsSnapshot value: // стала пустойScopedService IOptionsMonitor value: 0 // не измениласьTransientService IOptions value: 0TransientService IOptionsSnapshot value: // стала пустойTransientService IOptionsMonitor value: 0 // не изменилась

Следующий вывод в консоль (конфиг больше не менялся)


SingletonService IOptions value: 0SingletonService IOptionsSnapshot value: 0 // не измениласьSingletonService IOptionsMonitor value: 0 // не измениласьScopedService IOptions value: 0ScopedService IOptionsSnapshot value: changed setting // измениласьScopedService IOptionsMonitor value: 0 // не измениласьTransientService IOptions value: 0TransientService IOptionsSnapshot value: changed setting // измениласьTransientService IOptionsMonitor value: 0 // не изменилась

Последний вывод (конфиг также не менялся)


SingletonService IOptions value: 0SingletonService IOptionsSnapshot value: 0 // не измениласьSingletonService IOptionsMonitor value: changed setting // измениласьScopedService IOptions value: 0ScopedService IOptionsSnapshot value: changed setting // измениласьScopedService IOptionsMonitor value: changed setting // измениласьTransientService IOptions value: 0TransientService IOptionsSnapshot value: changed setting // измениласьTransientService IOptionsMonitor value: changed setting // изменилась

Что имеем в итоге? А имеем то, что IOptionsMonitor не такой шустрый, как нам говорит документация. Как можно заметить IOptionsSnapshot может вернуть пустое значение. Но, он работает быстрее, чем IOptionsMonitor.


Пока не особо понятно откуда берется это пустое значение. И самое интересное, что подобное поведение проявляется не всегда. Как-то через раз в моем примере IOptionsMonitor и IOptionsSnapshot отрабатывают одновременно.


Выводы


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


Если ваши конфигурации будут меняться, то тут все как всегда зависит. Если Вам важно, что бы в скоупе вашего запроса настройки были релевантны на момент запроса, IOptionsSnapshot ваш выбор (но не для Singletone, в нем значение никогда не изменится). Но стоит учитывать его странности, хотя и столкнуться с ними вряд ли вам придется.


Если же вам нужны наиболее актуальные значения (или почти) используйте IOptionsMonitor.


Буду рад, если вы запустите пример у себя, и расскажете, повторяется подобное поведение или нет. Возможно мы имеем баг на MacOS, а может это by design.


Продолжу разбираться с этой темой, а пока завел issue, может там прояснят такое поведение.

Источник: habr.com
К списку статей
Опубликовано: 20.06.2020 00:17:13
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Net

C

Ioptions

Ioptionssnapshot

Ioptionsmonitor

Config

Configuration

Asp.net core

Категории

Последние комментарии

© 2006-2020, personeltest.ru