При использовании разветвленной конфигурации приложения, например, в 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(). Первый метод возвращает готовый к использованию объект, второй — требует предварительного создания объекта, на который будут проецироваться настройки из конфигурации.
