При использовании разветвленной конфигурации приложения, например, в json-файлах с множеством параметров и секций бывает не всегда удобно использовать последовательный доступ к каждой настройке, используя индексатор IConfiguration
. В ASP.NET Core может использоваться проекция конфигурации на классы C#, чтобы представить сложную иерархическую структуру конфигурации приложения в виде обычного объекта. Для этого мы можем использовать методы Get<T>()
и Bind()
.
Пример использования методов Get<T>() и Bind()
До сих пор мы последовательно считывали необходимые нам настройки приложения используя свойство-индексатор IConfiguration
. Создадим новый проект ASP.NET Core Web API и добавим в файл appsettings.json следующую конфигурацию:
{ "Weather": { "Count": 3, "Min": 10, "Max": 30 }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
Секция Weather
также содержит настройки для работы контроллера WeatherForecastController
, но, в отличие от предыдущих примеров, здесь мы уже указываем три различных значения — Count
, Min
и Max
. Теперь наша задача состоит в том, чтобы получить все три настройки из конфигурации. Для этого создадим новый класс со свойствами, соответствующими настройкам, например:
public class Options { public int Count { get; set; } public int Min { get; set; } public int Max { get; set; } }
Все три свойства этого класса публичные и соответствуют как по названию, так и по типу настройкам из конфигурации. Перейдем в контроллер WeatherForecastController
и изменим его код следующим образом:
[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly IConfiguration _config; public WeatherForecastController(IConfiguration configuration) { _config = configuration; } [HttpGet] public IEnumerable<WeatherForecast> Get() { var options = _config.GetSection("Weather").Get<Options>(bindOpts=> { bindOpts.ErrorOnUnknownConfiguration = true; }); return Enumerable.Range(1, options.Count).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(options.Min, options.Max), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } }
Здесь мы запрашиваем через конструктор сервис IConfiguration, а в методе Get() в самом начале и происходит то, что называется «Проекция конфигурации на классы»
var options = _config.GetSection("Weather").Get<Options>(bindOpts=> { bindOpts.ErrorOnUnknownConfiguration = true; });
Здесь мы проецируем секцию «Weather» на класс Options
. В параметрах метода мы указываем настройки привязки и, в данном случае, мы указываем, что необходимо , сгенерировать исключение при преобразовании значения или при обнаружении ключа конфигурации, для которого в предоставленном объекте модели нет свойства, совпадающего с именем ключа. В результате выполнения метода Get<T>()
мы получаем уже готовый к использованию объект, который мы и используем далее для генерации массива объектов WeatherForecast
. Запустим приложение и проверим результат:
Ещё один вариант проецирования конфигурации на класс – использование метода Bind()
. Применительно к нашему примеру, проекция конфигурации на класс могла бы выглядеть следующим образом:
var options = new Options(); _config.GetSection("Weather").Bind(options, bindOpts => { bindOpts.ErrorOnUnknownConfiguration = true; });
То есть, в отличие от предыдущего метода, в метод Bind() мы передаем уже созданный объект. Также для метода Bind() имеется переопределенная версия, где мы можем сразу указать имя секции параметры которой необходимо спроецировать:
var options = new Options(); _config.Bind("Weather", options);
Привязка массива
Конфигурация может содержать в качестве настроек массив каких-либо значений. Чтобы определить массив значений в качестве настроек приложения, необходимо создать новую секцию с именем массива и расположить в этой секции необходимые настройки. Причем ключом настройки будет выступать порядковый номер элемента массива. Например, добавим в наш файл конфигурации следующий массив:
{ "Weather": { "Count": 3, "Min": 10, "Max": 30, "Summaries": { "0": "Нормально", "1": "Тепло", "2": "Жарко", "3": "Духота", "4": "Адская жара" } }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
Теперь изменим наш класс Options
:
public class Options { public int Count { get; set; } public int Min { get; set; } public int Max { get; set; } public string[] Summaries { get; set; } }
И теперь воспользуемся массивом из конфигурации в нашем приложении:
[HttpGet] public IEnumerable<WeatherForecast> Get() { var options = new Options(); _config.Bind("Weather", options); return Enumerable.Range(1, options.Count).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(options.Min, options.Max), Summary = options.Summaries[Random.Shared.Next(options.Summaries.Length)] }) .ToArray(); }
Запустим приложение и убедимся. что теперь контроллер выдает значения на русском языке:
Итого
При необходимости, мы можем проецировать конфигурацию на обычные классы C#. Для проецирования конфигурации на классы используются два метода — универсальный метод Get<T>()
и метод Bind()
. Первый метод возвращает готовый к использованию объект, второй — требует предварительного создания объекта, на который будут проецироваться настройки из конфигурации.