Поделиться через


Настройка в .NET

Настройка в .NET выполняется с помощью одного или нескольких поставщиков конфигурации . Поставщики конфигурации считывают данные конфигурации из пар ключ-значение через разные источники конфигурации.

  • Файлы параметров, например appsettings.json
  • Переменные среды
  • Azure Key Vault
  • Конфигурация приложений Azure
  • Аргументы командной строки
  • Пользовательские поставщики (установленные или созданные)
  • Файлы каталогов
  • Объекты .NET в памяти
  • Сторонние поставщики

Заметка

Сведения о настройке самой среды выполнения .NET см. в параметрах конфигурации среды выполнения .NET.

Основные понятия и абстракции

Учитывая один или несколько источников конфигурации, тип IConfiguration предоставляет единое представление данных конфигурации. Конфигурация доступна только для чтения, и шаблон конфигурации не предназначен для программной записи. Интерфейс IConfiguration является одним представлением всех источников конфигурации, как показано на следующей схеме:

Интерфейс IConfiguration является одним представлением всех источников конфигурации.

Настройка консольных приложений

Консольные приложения .NET, созданные с помощью нового шаблона команд dotnet или Visual Studio по умолчанию , не предоставляют возможности конфигурации. Чтобы добавить конфигурацию в новое консольное приложение .NET, добавьте ссылку на пакетв📦 Microsoft.Extensions.Configuration. Этот пакет является основой для настройки в приложениях .NET. Он предоставляет ConfigurationBuilder и связанные типы.

using Microsoft.Extensions.Configuration;

var configuration = new ConfigurationBuilder()
    .AddInMemoryCollection(new Dictionary<string, string?>()
    {
        ["SomeKey"] = "SomeValue"
    })
    .Build();

Console.WriteLine(configuration["SomeKey"]);

// Outputs:
//   SomeValue

Предыдущий код:

  • Создает новый экземпляр ConfigurationBuilder.
  • Добавляет коллекцию пар ключ-значение, хранящуюся в памяти, в построитель конфигурации.
  • Вызывает метод Build() для создания экземпляра IConfiguration.
  • Записывает значение ключа SomeKey в консоль.

Хотя в этом примере используется конфигурация в памяти, существует множество поставщиков конфигураций, предоставление функциональных возможностей для файловых переменных, переменных среды, аргументов командной строки и других источников конфигурации. Для получения дополнительной информации см. раздел Поставщики конфигурации в .NET.

Альтернативный подход к размещению

Как правило, ваши приложения будут делать больше, чем просто читать конфигурацию. Скорее всего, они будут использовать внедрение зависимостей, логирование и другие сервисы. Для приложений, использующих эти службы, рекомендуется подход .NET Generic Host. Вместо этого добавьте ссылку на пакет📦 Microsoft.Extensions.Hosting. Измените файл Program.cs, чтобы он соответствовал следующему коду:

using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateApplicationBuilder(args).Build();

// Application code should start here.

await host.RunAsync();

Метод Host.CreateApplicationBuilder(String[]) предоставляет конфигурацию по умолчанию для приложения в следующем порядке: от самого высокого до низкого приоритета:

  1. Аргументы командной строки, использующие провайдера конфигурации командной строки .
  2. Переменные среды с использованием поставщика конфигурации "Переменные среды" .
  3. секреты приложений при запуске приложения в среде Development.
  4. appsettings.Environment.json при использовании поставщика конфигурации JSON . Например, appsettings.appsettings Production.json и appsettings Development.json..
  5. appsettings.json с помощью поставщика конфигурации JSON.
  6. ChainedConfigurationProvider: добавляет существующий IConfiguration как источник.

Добавление поставщика конфигурации переопределяет предыдущие значения конфигурации. Например, Command-line configuration provider переопределяет все значения других поставщиков, так как он добавлен последним. Если SomeKey заданы как в appsettings.json, так и в среде, используется значение среды, так как оно было добавлено после appsettings.json.

Привязка

Одним из ключевых преимуществ использования абстракций конфигурации .NET является возможность привязки значений конфигурации к экземплярам объектов .NET. Например, поставщика конфигурации JSON можно использовать для сопоставления файлов appsettings.json с объектами .NET и использовать с внедрением зависимостей. Это позволяет использовать шаблон параметров , который использует классы для обеспечения строго типизированного доступа к группам связанных параметров. Привязка по умолчанию основана на отражении, но есть альтернативный генератор источников, который легко включить.

Конфигурация .NET предоставляет различные абстракции. Рассмотрим следующие интерфейсы:

  • IConfiguration: представляет набор свойств конфигурации приложения key/value.
  • IConfigurationRoot: представляет корень иерархии IConfiguration.
  • IConfigurationSection: представляет раздел значений конфигурации приложения.

Эти абстракции не зависят от базового поставщика конфигурации (IConfigurationProvider). Другими словами, можно использовать экземпляр IConfiguration для доступа к любому значению конфигурации от нескольких поставщиков.

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

  • Прямая десериализация (с помощью встроенных преобразователей) для примитивных типов.
  • TypeConverter для сложного типа, если тип имеет один.
  • Отражение для сложного типа, содержащего свойства.

Заметка

Папка имеет несколько ограничений:

  • Свойства игнорируются, если они имеют закрытые сеттеры или их тип нельзя преобразовать.
  • Свойства без соответствующих ключей конфигурации игнорируются.

Иерархии привязки

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

{
  "Parent": {
    "FavoriteNumber": 7,
    "Child": {
      "Name": "Example",
      "GrandChild": {
        "Age": 3
      }
    }
  }
}

В следующей таблице представлены примеры ключей и соответствующие значения для предыдущего примера JSON:

Ключ Ценность
"Parent:FavoriteNumber" 7
"Parent:Child:Name" "Example"
"Parent:Child:GrandChild:Age" 3

Сценарии расширенной привязки

При работе с определенными типами привязка конфигурации имеет определенное поведение и ограничения. В этом разделе содержатся следующие подразделы:

Привязка к словарям

При привязке конфигурации к Dictionary<TKey,TValue> месту, где значение является мутируемым типом коллекции (например, массивами или списками), повторяющиеся привязки к одному ключу расширяют значения коллекции вместо замены.

В следующем примере показано следующее поведение:

IConfiguration config = new ConfigurationBuilder()
    .AddInMemoryCollection()
    .Build();

config["Queue:0"] = "Value1";
var dict = new Dictionary<string, string[]>() { { "Queue", new[] { "InitialValue" } } };

Console.WriteLine("=== Dictionary Binding with Collection Values ===");
Console.WriteLine($"Initially: {string.Join(", ", dict["Queue"])}");

// In .NET 7+, binding extends the collection instead of replacing it.
config.Bind(dict);
Console.WriteLine($"After Bind: {string.Join(", ", dict["Queue"])}");

config["Queue:1"] = "Value2";
config.Bind(dict);
Console.WriteLine($"After 2nd Bind: {string.Join(", ", dict["Queue"])}");

Дополнительные сведения см. в разделе "Конфигурация привязки к словарю расширяет значения".

Ключи словаря с двоеточиями

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

  • Используйте альтернативные символы разделителя (например, двойные подчеркивания __) в ключах конфигурации и при необходимости преобразуйте их программным способом.
  • Вручную десериализуйте конфигурацию как сырой JSON с помощью System.Text.Json или аналогичной библиотеки, поддерживающей двоеточия в ключах.
  • Создайте пользовательский слой сопоставления, который преобразует безопасные ключи в необходимые ключи, используя двоеточия.

Привязка к типам IReadOnly*

Привязка конфигурации не поддерживает привязку непосредственно к интерфейсам коллекций IReadOnlyList<T>, IReadOnlyDictionary<TKey, TValue> или другим интерфейсам коллекций только для чтения. Этим интерфейсам не хватает механизмов, необходимых привязке для заполнения коллекций.

Чтобы работать с коллекциями только для чтения, используйте изменяемые типы для свойств, заполняемых привязкой, а затем предоставляйте их как интерфейсы только для чтения для потребителей:

Console.WriteLine("=== IReadOnly* Types (NOT Directly Supported) ===");

var readonlyConfig = new ConfigurationBuilder()
    .AddInMemoryCollection(new Dictionary<string, string?>
    {
        ["Settings:Values:0"] = "Item1",
        ["Settings:Values:1"] = "Item2",
        ["Settings:Values:2"] = "Item3",
    })
    .Build();

// This class uses List<string> for binding, exposes as IReadOnlyList<string>.
var settings = new SettingsWithReadOnly();
readonlyConfig.GetSection("Settings").Bind(settings);

Console.WriteLine("Values bound to mutable List, exposed as IReadOnlyList:");
foreach (var value in settings.ValuesReadOnly)
{
    Console.WriteLine($"  {value}");
}

Реализация класса конфигурации:

class SettingsWithReadOnly
{
    // Use mutable type for binding
    public List<string> Values { get; set; } = [];

    // Expose as read-only for consumers
    public IReadOnlyList<string> ValuesReadOnly => Values;
}

Этот подход позволяет связывателю заполнять изменяемый List<string>, при показе неизменяемого интерфейса потребителям через IReadOnlyList<string>.

Привязка с параметризуемыми конструкторами

Начиная с .NET 7, привязчик конфигурации поддерживает привязку к типам с одним общедоступным параметризованным конструктором. Это позволяет заполнять неизменяемые типы и записи непосредственно из конфигурации:

Console.WriteLine("=== Parameterized Constructor Binding ===");

var ctorConfig = new ConfigurationBuilder()
    .AddInMemoryCollection(new Dictionary<string, string?>
    {
        ["AppSettings:Name"] = "MyApp",
        ["AppSettings:MaxConnections"] = "100",
        ["AppSettings:Timeout"] = "30"
    })
    .Build();

// Binding to a type with a single parameterized constructor
var appSettings = ctorConfig.GetSection("AppSettings").Get<AppSettings>();
if (appSettings != null)
{
    Console.WriteLine($"Name: {appSettings.Name}");
    Console.WriteLine($"MaxConnections: {appSettings.MaxConnections}");
    Console.WriteLine($"Timeout: {appSettings.Timeout}");
}

Класс неизменяемых параметров:

// Immutable type with single parameterized constructor.
class AppSettings
{
    public string Name { get; }
    public int MaxConnections { get; }
    public int Timeout { get; }

    public AppSettings(string name, int maxConnections, int timeout)
    {
        Name = name;
        MaxConnections = maxConnections;
        Timeout = timeout;
    }
}

Это важно

Привязыватель поддерживает только типы с одним общедоступным параметризованным конструктором. Если тип имеет несколько открытых параметризованных конструкторов, привязка не может определить, какой из них следует использовать, и привязка завершится ошибкой. Используйте один параметризованный конструктор или конструктор без параметров с помощью наборов свойств.

Базовый пример

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

Совет

Тип System.Configuration.ConfigurationBuilder отличается от типа Microsoft.Extensions.Configuration.ConfigurationBuilder. Всё это содержимое относится к пакетам и пространствам имен Microsoft.Extensions.* NuGet.

Рассмотрим следующий проект C#:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <Content Include="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.3" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.3" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.3" />
  </ItemGroup>

</Project>

Файл проекта, приведенный выше, ссылается на несколько конфигурационных пакетов NuGet.

Рассмотрим пример файла appsettings.json:

{
    "Settings": {
        "KeyOne": 1,
        "KeyTwo": true,
        "KeyThree": {
            "Message": "Oh, that's nice...",
            "SupportedVersions": {
                "v1": "1.0.0",
                "v3": "3.0.7"
            }
        },
        "IPAddressRange": [
            "46.36.198.121",
            "46.36.198.122",
            "46.36.198.123",
            "46.36.198.124",
            "46.36.198.125"
        ]
    }
}

Теперь, учитывая этот JSON-файл, ниже приведен пример шаблона потребления с помощью построителя конфигураций напрямую:

using Microsoft.Extensions.Configuration;

// Build a config object, using env vars and JSON providers.
IConfigurationRoot config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .AddEnvironmentVariables()
    .Build();

// Get values from the config given their key and their target type.
Settings? settings = config.GetRequiredSection("Settings").Get<Settings>();

// Write the values to the console.
Console.WriteLine($"KeyOne = {settings?.KeyOne}");
Console.WriteLine($"KeyTwo = {settings?.KeyTwo}");
Console.WriteLine($"KeyThree:Message = {settings?.KeyThree?.Message}");

// Application code which might rely on the config could start here.

// This will output the following:
//   KeyOne = 1
//   KeyTwo = True
//   KeyThree:Message = Oh, that's nice...

Предыдущий код C#:

  • Создает экземпляр объекта ConfigurationBuilder.
  • Добавляет файл "appsettings.json" для распознавания поставщиком конфигурации JSON.
  • Добавляет переменные среды, которые распознаются провайдером конфигурации переменных среды.
  • Получает требуемый раздел "Settings" и соответствующий экземпляр Settings с использованием экземпляра config.

Объект Settings формируется следующим образом:

public sealed class Settings
{
    public required int KeyOne { get; set; }
    public required bool KeyTwo { get; set; }
    public required NestedSettings KeyThree { get; set; } = null!;
}
public sealed class NestedSettings
{
    public required string Message { get; set; } = null!;
}

Базовый пример с хостингом

Чтобы получить доступ к значению IConfiguration, можно снова полагаться на пакет NuGet Microsoft.Extensions.Hosting. Создайте консольное приложение и вставьте в него следующее содержимое файла проекта:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <Content Include="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.3" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
  </ItemGroup>

</Project>

Предыдущий файл проекта определяет следующее:

  • Приложение — это исполняемый файл.
  • Файл appsettings.json должен быть скопирован в выходной каталог при компиляции проекта.
  • Добавлена ссылка на пакет NuGet Microsoft.Extensions.Hosting.

Добавьте файл appsettings.json в корне проекта со следующим содержимым:

{
    "KeyOne": 1,
    "KeyTwo": true,
    "KeyThree": {
        "Message": "Thanks for checking this out!"
    }
}

Замените содержимое файла Program.cs следующим кодом C#:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateApplicationBuilder(args).Build();

// Ask the service provider for the configuration abstraction.
IConfiguration config = host.Services.GetRequiredService<IConfiguration>();

// Get values from the config given their key and their target type.
int keyOneValue = config.GetValue<int>("KeyOne");
bool keyTwoValue = config.GetValue<bool>("KeyTwo");
string? keyThreeNestedValue = config.GetValue<string>("KeyThree:Message");

// Write the values to the console.
Console.WriteLine($"KeyOne = {keyOneValue}");
Console.WriteLine($"KeyTwo = {keyTwoValue}");
Console.WriteLine($"KeyThree:Message = {keyThreeNestedValue}");

// Application code which might rely on the config could start here.

await host.RunAsync();

// This will output the following:
//   KeyOne = 1
//   KeyTwo = True
//   KeyThree:Message = Thanks for checking this out!

При запуске этого приложения Host.CreateApplicationBuilder определяет поведение для обнаружения конфигурации JSON и её предоставления через экземпляр IConfiguration. Из экземпляра host можно запросить у поставщика услуг экземпляр IConfiguration, а затем получить у него значения.

Совет

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

Базовый пример размещения и использования API индексатора

Рассмотрим то же appsettings.json содержимое файла из предыдущего примера:

{
    "SupportedVersions": {
        "v1": "1.0.0",
        "v3": "3.0.7"
    },
    "IPAddressRange": [
        "46.36.198.123",
        "46.36.198.124",
        "46.36.198.125"
    ]
}

Замените содержимое файла Program.cs следующим кодом C#:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateApplicationBuilder(args).Build();

// Ask the service provider for the configuration abstraction.
IConfiguration config = host.Services.GetRequiredService<IConfiguration>();

// Get values from the config given their key and their target type.
string? ipOne = config["IPAddressRange:0"];
string? ipTwo = config["IPAddressRange:1"];
string? ipThree = config["IPAddressRange:2"];
string? versionOne = config["SupportedVersions:v1"];
string? versionThree = config["SupportedVersions:v3"];

// Write the values to the console.
Console.WriteLine($"IPAddressRange:0 = {ipOne}");
Console.WriteLine($"IPAddressRange:1 = {ipTwo}");
Console.WriteLine($"IPAddressRange:2 = {ipThree}");
Console.WriteLine($"SupportedVersions:v1 = {versionOne}");
Console.WriteLine($"SupportedVersions:v3 = {versionThree}");

// Application code which might rely on the config could start here.

await host.RunAsync();

// This will output the following:
//     IPAddressRange:0 = 46.36.198.123
//     IPAddressRange:1 = 46.36.198.124
//     IPAddressRange:2 = 46.36.198.125
//     SupportedVersions:v1 = 1.0.0
//     SupportedVersions:v3 = 3.0.7

К значениям обращаются с помощью API индексатора, где каждый ключ является строкой, а значение — строка. Конфигурация поддерживает свойства, объекты, массивы и словари.

Поставщики конфигураций

В следующей таблице показаны поставщики конфигурации, доступные для приложений .NET Core.

Поставщик конфигурации Предоставляет параметры настройки из
Конфигурация приложений Azure Конфигурация приложений Azure
Azure Key Vault Azure Key Vault
командная строка Параметры командной строки
Настройка Настраиваемый источник
переменные среды Переменные среды
Файл ФАЙЛЫ JSON, XML и INI
Ключ для каждого файла Файлы каталогов
Память Коллекции в памяти
Секреты приложений (диспетчер секретов) Файл в каталоге профилей пользователя

Совет

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

Дополнительные сведения о различных поставщиках конфигурации см. в разделе Поставщики конфигурации в .NET.

См. также