Конфигурация ASP.NET Core приложения через IOptions

Долгое время в приложениях .NET для работы с файлами конфигурации использовался объект ConfigurationManager, а каждая секция в конфигурационном файле описывалась через ConfigurationSection. В ASP.NET Core подходы к работе с конфигурационными файлами изменились и на смену пришел интерфейс IOptions.

Интерфейс IOptions

ASP.NET Core позволяет описывать конфигурацию в виде простых .NET-объектов, освобождая нас от необходимости использовать дополнительные атрибуты и наследование (как это было с ConfigurationSection). Доступ к конфигурации в таком случае осуществляется путем внедрением зависимости IOptions<> в нужный компонент.

Для пример создадим объект с нужными для нас свойствами конфигруации:

public class OwnerOptions
{
  public string Account { get; set; }

  public string DisplayName { get; set; }

  public string Email { get; set; }
}

Для получения доступа к конфигурации, например, из контроллера достаточно будет просто внедрить IOptions<> в контроллер:

public class HomeController : Controller
{
  private readonly IOptions<OwnerOptions> _ownerOptions;

  public HomeController(IOptions<OwnerOptions> ownerOptions)
  {
      _ownerOptions = ownerOptions;
  }

Доступ к свойствам этого объекта осуществляется через свойство Value:

  public IActionResult Index()
  {
      return View(new
                  {
                      Account = _ownerOptions.Value.Account
                  });
  }

Настройка объектов конфигурации

Заполние содержимым options-объектов происходит в момент запуска приложения. В startup.cs для этого существует специальный метод ConfigureServices().

Чтобы сконфигурировать нужный объект следует воспользоваться методом Configure<>() интерфейса IServiceCollection:

public void ConfigureServices(IServiceCollection services)
{
  services.Configure<OwnerOptions>(...);
}

В зависимости от ситуации настраивать объекты конфигурации можно разными способами. В каждом случае используется своя перегрузка метода Configure().

Заполнение конфигурации непосредственно из кода

Для заполнения значений из кода в метод Configure() следует передать Action, внутри которого задаются нужные значения:

public class OwnerOptions
{
  public string Account { get; set; }

  public string DisplayName { get; set; }

  public string Email { get; set; }
}

public class Startup
{
  public void ConfigureServices(IServiceCollection services)
  {
    services.Configure<OwnerOptions>(options =>
    {
      options.Account = "local";
      options.DisplayName = "Local account";
      options.Email = "[email protected]";
    });

Заполнение конфигурации из конфиг-файлов

Для заполнения значений из конфигурационного файла в метод Configure() следует передать свойство Configuration, которое определено в классе Startup:

public class WebsiteOptions
{
  public string Domain { get; set; }
}

public class Startup
{
  public IConfigurationRoot Configuration { get; }

  public void ConfigureServices(IServiceCollection services)
  {
    services.Configure<WebsiteOptions>(Configuration);

В этом случае значения для каждого свойства options-объекта будут сопоставляться ключам в конфигурационном файле. Поэтому в стандартный конфигурационный файл appsettings.json добавим свойство Domain, которое присутствует в WebsiteOptions.

{
  "Domain": "localhost"
}

Заполнение конфигурации из отдельной секции конфиг-файла

Этот случай очень похож на предыдущий за исключением того, что options-объект заполняется данными нужной конфигурационной секции. В метод Configure() передается не весь объект Configuration, а только конкретная его секция:

public class ConnectionStringOptions
{
  public string DefaultConnection { get; set; }
}

public class Startup
{
  public IConfigurationRoot Configuration { get; }

  public void ConfigureServices(IServiceCollection services)
  {
    services.Configure<ConnectionStringOptions>(Configuration.GetSection("ConnectionStrings"));

Файл appsettings.json будет иметь следующий вид:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssql;Database=aspnet-webapp;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

Заключение

Интерфейс IOptions предоставляет простой и в то же время гибкий способ определения конфигурационных параметров приложения. Это — эволюция объекта ConfigurationSection, использовавшегося до текущего момента.

Полная версия приложения, использующего одновременно три способа:

startup.cs

public class Startup
{
  public IConfigurationRoot Configuration { get; }

  public void ConfigureServices(IServiceCollection services)
  {
    services.Configure<OwnerOptions>(options =>
    {
        options.Account = "local";
        options.DisplayName = "Local account";
        options.Email = "[email protected]";
    });

    services.Configure<WebsiteOptions>(Configuration);

    services.Configure<ConnectionStringOptions>(
        Configuration.GetSection("ConnectionStrings"));
  }

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssql;Database=aspnet-webapp;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Domain": "localhost"
}

HomeController.cs

public class HomeController : Controller
{
  private readonly IOptions<OwnerOptions> _ownerOptions;
  private readonly IOptions<WebsiteOptions> _websiteOptions;
  private readonly IOptions<ConnectionStringOptions> _connectionStringOptions;

  public HomeController(IOptions<OwnerOptions> ownerOptions,
                        IOptions<WebsiteOptions> websiteOptions,
                        IOptions<ConnectionStringOptions> connectionStringOptions)
  {
    _ownerOptions = ownerOptions;
    _websiteOptions = websiteOptions;
    _connectionStringOptions = connectionStringOptions;
  }

  public IActionResult Index()
  {
    return View(new IndexViewModel
    {
        Owner = _ownerOptions.Value,
        Website = _websiteOptions.Value,
        ConnectionString = _connectionStringOptions.Value
    });
  }