Udostępnij przez


Konfiguracja na platformie .NET

Konfiguracja na platformie .NET jest wykonywana przy użyciu co najmniej jednego dostawcy konfiguracji . Dostawcy konfiguracji odczytują dane konfiguracji z par klucz-wartość przy użyciu różnych źródeł konfiguracji:

  • Pliki ustawień, takie jak appsettings.json
  • Zmienne środowiskowe
  • Azure Key Vault
  • Konfiguracja aplikacji Azure
  • Argumenty wiersza polecenia
  • Dostawcy niestandardowi (zainstalowani lub utworzeni)
  • Pliki katalogów
  • Obiekty .NET w pamięci
  • Zewnętrzni dostawcy

Uwaga

Aby uzyskać informacje na temat konfigurowania samego środowiska uruchomieniowego platformy .NET, zobacz ustawienia konfiguracji środowiska uruchomieniowego platformy .NET.

Pojęcia i abstrakcje

Biorąc pod uwagę co najmniej jedno źródło konfiguracji, typ IConfiguration zapewnia ujednolicony widok danych konfiguracji. Konfiguracja jest tylko do odczytu, a wzorzec konfiguracji nie jest przeznaczony do programowego zapisu. Interfejs IConfiguration jest pojedynczą reprezentacją wszystkich źródeł konfiguracji, jak pokazano na poniższym diagramie:

Interfejs

Konfigurowanie aplikacji konsoli

Aplikacje konsolowe platformy .NET utworzone przy użyciu nowego szablonu polecenia dotnet lub programu Visual Studio domyślnie nie uwidaczniają możliwości konfiguracji. Aby dodać konfigurację w nowej aplikacji konsolowej platformy .NET, dodaj odwołanie do 📦 pakietu Microsoft.Extensions.Configuration. Ten pakiet jest podstawą konfiguracji w aplikacjach platformy .NET. Udostępnia on ConfigurationBuilder i powiązane typy.

using Microsoft.Extensions.Configuration;

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

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

// Outputs:
//   SomeValue

Powyższy kod:

  • Tworzy nowy obiekt ConfigurationBuilder.
  • Dodaje do modułu konfiguracyjnego kolekcję par klucz-wartość przechowywaną w pamięci.
  • Wywołuje metodę Build() w celu utworzenia wystąpienia IConfiguration.
  • Zapisuje wartość klucza SomeKey do konsoli.

W tym przykładzie użyto konfiguracji w pamięci, ale dostępnych jest wielu dostawców konfiguracji, udostępniając funkcje oparte na plikach, zmiennych środowiskowych, argumentów wiersza polecenia i innych źródeł konfiguracji. Aby uzyskać więcej informacji, zobacz Dostawcy konfiguracji na platformie .NET.

Alternatywne podejście do hostingu

Często twoje aplikacje robią więcej niż tylko odczytywanie konfiguracji. Prawdopodobnie będą używać wstrzykiwania zależności, logowania i innych usług. Podejście do hosta generycznego .NET jest zalecane w przypadku aplikacji korzystających z tych usług. Zamiast tego rozważ dodanie odwołania do pakietu do 📦 microsoft.Extensions.Hosting. Zmodyfikuj plik Program.cs, aby był zgodny z następującym kodem:

using Microsoft.Extensions.Hosting;

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

// Application code should start here.

await host.RunAsync();

Metoda Host.CreateApplicationBuilder(String[]) zapewnia domyślną konfigurację aplikacji w następującej kolejności, od najwyższego do najniższego priorytetu:

  1. Argumenty wiersza polecenia, które korzystają z dostawcy konfiguracji wiersza polecenia.
  2. Zmienne środowiskowe przy użyciu dostawcy konfiguracji zmiennych środowiskowych .
  3. Sekrety aplikacji, gdy aplikacja działa w środowisku Development.
  4. ustawienia aplikacji.Environment.json przy użyciu dostawcy konfiguracji JSON . Na przykład appsettings.Production.json i appsettings.Development.json.
  5. appsettings.json przy użyciu dostawcy konfiguracji JSON.
  6. ChainedConfigurationProvider : dodaje istniejący IConfiguration jako źródło.

Dodanie dostawcy konfiguracji zastępuje poprzednie wartości konfiguracji. Na przykład dostawca konfiguracji wiersza polecenia zastępuje wszystkie wartości innych dostawców, ponieważ jest dodawany jako ostatni. Jeśli SomeKey jest ustawiona zarówno w appsettings.json, jak i w środowisku, zostanie użyta wartość środowiska, ponieważ została dodana po appsettings.json.

Binding

Jedną z kluczowych zalet korzystania z abstrakcji konfiguracji platformy .NET jest możliwość powiązania wartości konfiguracji z wystąpieniami obiektów platformy .NET. Na przykład dostawca konfiguracji JSON może służyć do mapowania plików appsettings.json na obiekty platformy .NET i jest używany z wstrzykiwaniem zależności. Umożliwia to wzorzec opcji, który używa klas w celu zapewnienia silnie typizowanego dostępu do grup powiązanych ustawień. Domyślny binder jest oparty na odbiciu, ale istnieje generator źródła alternatywny, który jest łatwy do włączenia.

Konfiguracja platformy .NET zapewnia różne abstrakcje. Rozważ następujące interfejsy:

Te abstrakcje są niezależne od podstawowego dostawcy konfiguracji (IConfigurationProvider). Innymi słowy, możesz użyć wystąpienia IConfiguration, aby uzyskać dostęp do dowolnej wartości konfiguracji od wielu dostawców.

Binder może używać różnych metod przetwarzania wartości parametrów konfiguracyjnych.

  • Deserializacja bezpośrednia (przy użyciu wbudowanych konwerterów) dla typów pierwotnych.
  • Dla TypeConverter typu złożonego, gdy typ ma jeden.
  • Odbicie dla typu złożonego, który ma właściwości.

Uwaga

Segregator ma kilka ograniczeń:

  • Właściwości są ignorowane, jeśli mają prywatne settery lub ich typ nie może zostać przekonwertowany.
  • Właściwości bez odpowiednich kluczy konfiguracji są ignorowane.

Hierarchie wiązań

Wartości konfiguracji mogą zawierać dane hierarchiczne. Obiekty hierarchiczne są reprezentowane przy użyciu ogranicznika : w kluczach konfiguracji. Aby uzyskać dostęp do wartości konfiguracji, użyj znaku :, aby rozdzielić hierarchię. Rozważmy na przykład następujące wartości konfiguracji:

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

W poniższej tabeli przedstawiono przykładowe klucze i odpowiadające im wartości dla poprzedniego przykładowego kodu JSON:

Key Wartość
"Parent:FavoriteNumber" 7
"Parent:Child:Name" "Example"
"Parent:Child:GrandChild:Age" 3

Zaawansowane scenariusze powiązań

Powiązanie konfiguracji ma określone zachowania i ograniczenia podczas pracy z określonymi typami. Ta sekcja zawiera następujące podsekcje:

Wiązanie ze słownikami

Po powiązaniu konfiguracji z obiektem Dictionary<TKey,TValue> , w którym wartość jest modyfikowalnym typem kolekcji (takim jak tablice lub listy), powtarzające się powiązania z tym samym kluczem rozszerzają wartości kolekcji zamiast ich zastępowania.

W poniższym przykładzie pokazano to zachowanie:

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"])}");

Aby uzyskać więcej informacji, zobacz Binding config to dictionary extends values (Powiązanie konfiguracji do słownika rozszerza wartości).

Klucze słownika z dwukropkami

Znak dwukropka (:) jest zarezerwowany jako ogranicznik hierarchii w kluczach konfiguracji. Oznacza to, że wiążąc konfigurację, nie można używać dwukropków w kluczach słownika. Jeśli klucze zawierają dwukropki (takie jak adresy URL lub inne sformatowane identyfikatory), system konfiguracji interpretuje je jako ścieżki hierarchiczne, a nie dosłowne znaki. Rozważ następujące obejścia:

  • Użyj alternatywnych znaków ograniczników (takich jak podwójne podkreślenia ) w kluczach __konfiguracji i w razie potrzeby przekształć je programowo.
  • Ręcznie deserializuj konfigurację jako surowy JSON przy użyciu System.Text.Json lub podobnej biblioteki, która obsługuje dwukropki w kluczach.
  • Utwórz niestandardową warstwę mapowania, która tłumaczy bezpieczne klucze na żądane klucze z użyciem dwukropków.

Wiązanie z typami IReadOnly*

Narzędzie do wiązania konfiguracji nie obsługuje wiązania bezpośrednio do IReadOnlyList<T>, IReadOnlyDictionary<TKey, TValue> ani innych interfejsów kolekcji tylko do odczytu. Te interfejsy nie mają mechanizmów, które są potrzebne powiązywaczowi do wypełniania kolekcji.

Aby pracować z kolekcjami tylko do odczytu, użyj typów modyfikowalnych dla właściwości, które wypełnia powiązanie, a następnie udostępnij je jako interfejsy tylko do odczytu dla użytkowników.

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}");
}

Implementacja klasy konfiguracji:

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

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

Takie podejście umożliwia binderowi wypełnienie modyfikowalnego elementu List<string> podczas prezentowania niezmiennego interfejsu użytkownikom za pośrednictwem interfejsu IReadOnlyList<string>.

Powiązanie za pomocą konstruktorów sparametryzowanych

Od wersji .NET 7, mechanizm powiązania konfiguracji obsługuje typy z jednym publicznym konstruktorem sparametryzowanym. Dzięki temu typy i rekordy niezmienne mogą być wypełniane bezpośrednio z konfiguracji:

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}");
}

Niezmienna klasa ustawień:

// 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;
    }
}

Ważne

Binder obsługuje tylko typy z jednym publicznym konstruktorem sparametryzowanym. Jeśli typ ma wiele publicznych konstruktorów sparametryzowanych, binder nie może określić, którego z nich użyć, a powiązanie zakończy się niepowodzeniem. Użyj jednego konstruktora sparametryzowanego lub konstruktora bez parametrów z zestawami właściwości.

Przykład podstawowy

Aby uzyskać dostęp do wartości konfiguracji w ich podstawowej formie, bez pomocy metody ogólnego hosta, użyj typu ConfigurationBuilder bezpośrednio.

Tip

Typ System.Configuration.ConfigurationBuilder różni się od typu Microsoft.Extensions.Configuration.ConfigurationBuilder. Cała ta zawartość jest specyficzna dla pakietów NuGet i przestrzeni nazw Microsoft.Extensions.*.

Rozważmy następujący projekt w języku 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>

Poprzedni plik projektu odwołuje się do kilku pakietów NuGet do konfiguracji.

Rozważmy przykładowy plik 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"
        ]
    }
}

Teraz, biorąc pod uwagę ten plik JSON, oto przykładowy wzorzec zużycia bezpośrednio przy użyciu konstruktora konfiguracji:

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...

Poprzedni kod języka C#:

  • Tworzy wystąpienie elementu ConfigurationBuilder.
  • Dodaje plik "appsettings.json" do rozpoznawania przez dostawcę konfiguracji JSON.
  • Dodaje zmienne środowiskowe rozpoznawane przez dostawcę konfiguracji zmiennych środowiskowych.
  • Wymaganą sekcję "Settings" i odpowiednie wystąpienie Settings pobiera się przy użyciu wystąpienia config.

Obiekt Settings jest kształtowany w następujący sposób:

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!;
}

Podstawowy przykład hostingu

Aby uzyskać dostęp do wartości IConfiguration, możesz ponownie opierać się na pakiecie Microsoft.Extensions.Hosting NuGet. Utwórz nową aplikację konsolową i wklej do niej następującą zawartość pliku projektu:

<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>

Powyższy plik projektu definiuje następujące elementy:

  • Aplikacja jest plikiem wykonywalnym.
  • Plik appsettings.json ma zostać skopiowany do katalogu wyjściowego podczas kompilowania projektu.
  • Dodano odwołanie do pakietu NuGet Microsoft.Extensions.Hosting.

Dodaj plik appsettings.json w katalogu głównym projektu z następującą zawartością:

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

Zastąp zawartość pliku Program.cs następującym kodem 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!

Po uruchomieniu tej aplikacji Host.CreateApplicationBuilder definiuje zachowanie w celu odnajdywania konfiguracji JSON i uwidaczniania jej za pośrednictwem wystąpienia IConfiguration. Z instancji host można poprosić dostawcę usług o instancję IConfiguration, a następnie pobrać z niej wartości.

Tip

Użycie pierwotnego IConfiguration wystąpienia w ten sposób, choć wygodne, nie jest bardzo dobrze skalowane. Gdy aplikacje stają się coraz bardziej złożone, a ich odpowiednie konfiguracje stają się bardziej złożone, zalecamy użycie wzorca opcji jako alternatywy.

Podstawowy przykład hostowania i używania interfejsu API indeksatora

Rozważ tę samą zawartość pliku appsettings.json z poprzedniego przykładu:

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

Zastąp zawartość pliku Program.cs następującym kodem 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

Do wartości uzyskuje się dostęp przy użyciu interfejsu API indeksatora, w którym każdy klucz jest ciągiem, a wartość jest ciągiem. Konfiguracja obsługuje właściwości, obiekty, tablice i słowniki.

Dostawcy konfiguracji

W poniższej tabeli przedstawiono dostawców konfiguracji dostępnych dla aplikacji platformy .NET Core.

Dostawca konfiguracji Dostarcza konfigurację z
Konfiguracja aplikacji Azure Azure App Configuration
Azure Key Vault Azure Key Vault
wiersza polecenia Parametry wiersza polecenia
Niestandardowe Rozwiązanie Źródło niestandardowe
Zmienne środowiskowe Zmienne środowiskowe
Plik Pliki JSON, XML i INI
Klucz do każdego pliku Pliki katalogów
Pamięć Kolekcje przechowywane w pamięci
Tajemnice aplikacji (menedżer tajemnic) Plik w katalogu profilu użytkownika

Tip

Kolejność dodawania dostawców konfiguracji ma znaczenie. Jeśli jest używanych wielu dostawców konfiguracji i więcej niż jeden dostawca określa ten sam klucz, zostanie użyty ostatni dodany.

Aby uzyskać więcej informacji na temat różnych dostawców konfiguracji, zobacz Dostawcy konfiguracji na platformie .NET.

Zobacz też