Configurazione in .NET

La configurazione in .NET viene eseguita usando uno o più provider di configurazione. I provider di configurazione leggono i dati di configurazione da coppie chiave-valore usando varie origini di configurazione:

  • File di impostazioni, ad esempio appsettings.json
  • Variabili di ambiente
  • Azure Key Vault
  • Configurazione app di Azure
  • Argomenti della riga di comando
  • Provider personalizzati, installati o creati
  • File della directory
  • Oggetti .NET in memoria
  • Provider di terzi

Nota

Per informazioni sulla configurazione del runtime .NET stesso, consultare impostazioni di configurazione del runtime .NET.

Concetti e astrazioni

Data una o più origini di configurazione, il tipo IConfiguration fornisce una visualizzazione unificata dei dati di configurazione. La configurazione è di sola lettura e il modello di configurazione non è progettato per essere scrivibile a livello di codice. L'interfaccia IConfiguration è una singola rappresentazione di tutte le origini di configurazione, come illustrato nel diagramma seguente:

The `IConfiguration` interface is a single representation of all the configuration sources.

Configurare le app console

Le applicazioni console .NET create usando il modello di comando dotnet new o Visual Studio, per impostazione predefinita non espongono funzionalità di configurazione. Per aggiungere una configurazione in una nuova applicazione console .NET, aggiungere un riferimento al pacchetto a Microsoft.Extensions.Configuration. Questo pacchetto offre gli elementi di base per la configurazione in app .NET. Fornisce infatti la classe ConfigurationBuilder e i tipi correlati.

using Microsoft.Extensions.Configuration;

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

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

// Outputs:
//   SomeValue

Il codice precedente:

  • Crea una nuova istanza di ConfigurationBuilder.
  • Aggiunge una raccolta in memoria di coppie chiave-valore al generatore di configurazione.
  • Chiama il metodo Build() per creare un'istanza di IConfiguration.
  • Scrive il valore della chiave SomeKey nella console.

Anche se in questo esempio viene usata una configurazione in memoria, sono disponibili molti provider di configurazione che espongono funzionalità per variabili di ambiente, argomenti della riga di comando e altre origini di configurazione basate su file. Per altre informazioni, vedere Provider di configurazione in .NET.

Approccio alternativo all'hosting

In genere, le app non si limiteranno a leggere una configurazione, ma probabilmente useranno anche funzionalità di inserimento delle dipendenze, strumenti di registrazione e altri servizi. Per le app che usano questi servizi è consigliato l'approccio host generico .NET. Valutare invece la possibilità di aggiungere un riferimento al pacchetto a Microsoft.Extensions.Hosting. Modificare il file Program.cs in modo che corrisponda al codice seguente:

using Microsoft.Extensions.Hosting;

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

// Application code should start here.

await host.RunAsync();

Il metodo Host.CreateApplicationBuilder(String[]) fornisce la configurazione predefinita per l'app nell'ordine seguente, dalla priorità più alta alla più bassa:

  1. Argomenti della riga di comando che usano il provider di configurazione della riga di comando.
  2. Variabili di ambiente che usano il provider di configurazione delle variabili di ambiente.
  3. Segreti dell'app quando l'app viene eseguita nell'ambiente Development.
  4. appsettings.json usando il provider di configurazione JSON.
  5. appsettings.Environment.json usando il provider di configurazione JSON. Ad esempio, appsettings.Produzione.json e appsettings.Sviluppo.json.
  6. ChainedConfigurationProvider: aggiunge un IConfiguration esistente come origine.

L'aggiunta di un provider di configurazione sostituisce i valori di configurazione precedenti. Ad esempio, il provider di configurazione della riga di comando esegue l'override di tutti i valori di altri provider perché è stato aggiunto per ultimo. Se SomeKey è impostato sia in appsettings.json che nell'ambiente, il valore dell'ambiente viene usato perché è stato aggiunto dopo appsettings.json.

Binding

Uno dei vantaggi principali dell'uso delle astrazioni di configurazione .NET è la possibilità di associare valori di configurazione a istanze di oggetti .NET. Ad esempio, il provider di configurazione JSON può essere usato per eseguire il mapping dei file appsettings.json agli oggetti .NET e viene usato con l'inserimento delle dipendenze. In questo modo il modello di opzioni, che usa le classi per fornire l'accesso fortemente tipizzato ai gruppi di impostazioni correlate. La configurazione di .NET fornisce diverse astrazioni. Si considerino le interfacce seguenti:

  • IConfiguration: rappresenta un set di proprietà di configurazione dell'applicazione di tipo chiave/valore.
  • IConfigurationRoot: rappresenta la radice di una gerarchia IConfiguration.
  • IConfigurationSection: rappresenta una sezione dei valori di configurazione dell'applicazione.

Queste astrazioni sono indipendenti dal provider di configurazione sottostante (IConfigurationProvider). In altre parole, è possibile usare un'istanza IConfiguration di per accedere a qualsiasi valore di configurazione da più provider.

Il binder può usare approcci diversi per elaborare i valori di configurazione:

  • Deserializzazione diretta (usando convertitori predefiniti) per i tipi primitivi.
  • Oggetto TypeConverter per un tipo complesso quando il tipo ne ha uno.
  • Reflection per un tipo complesso con proprietà.

Nota

Il binder presenta alcune limitazioni:

  • Le proprietà vengono ignorate se dispongono di setter privati o se il tipo non può essere convertito.
  • Le proprietà senza le chiavi di configurazione corrispondenti vengono ignorate.

Gerarchie binding

I valori di configurazione possono contenere dati gerarchici. Gli oggetti gerarchici vengono rappresentati con l'uso del delimitatore : nelle chiavi di configurazione. Per accedere a un valore di configurazione, usare il carattere : per delimitare una gerarchia. Si considerino ad esempio i valori di configurazione seguenti:

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

La tabella seguente rappresenta le chiavi di esempio e i relativi valori corrispondenti per l'esempio JSON precedente:

Chiave valore
"Parent:FavoriteNumber" 7
"Parent:Child:Name" "Example"
"Parent:Child:GrandChild:Age" 3

Esempio di base

Per accedere ai valori di configurazione nel formato di base, senza l'assistenza dell'approccio host generico, usare direttamente il tipo ConfigurationBuilder.

Suggerimento

Il tipo di System.Configuration.ConfigurationBuilder è diverso dal tipo di Microsoft.Extensions.Configuration.ConfigurationBuilder. Tutto questo contenuto è specifico per i pacchetti NuGet e gli spazi dei nomi di Microsoft.Extensions.*.

Considerare il progetto C# seguente:

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.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="8.0.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
  </ItemGroup>

</Project>

Il file di progetto precedente fa riferimento a diversi pacchetti NuGet di configurazione:

Si consideri un esempio appsettings.json file:

{
    "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"
        ]
    }
}

Dato questo file JSON, segue un modello di utilizzo di esempio usando direttamente il generatore di configurazione:

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

Il codice C# precedente:

  • Crea un'istanza di ConfigurationBuilder.
  • Aggiunge il file "appsettings.json" da riconoscere dal provider di configurazione JSON.
  • Aggiunge le variabili di ambiente riconosciute dal provider di configurazione delle variabili di ambiente.
  • Ottiene la sezione "Settings" richiesta e l'istanza di Settings corrispondente utilizzando l'istanza di config.

L'oggetto Settings viene definito come segue:

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

Esempio di base con hosting

Per accedere al valore IConfiguration, è possibile fare nuovamente affidamento sul pacchetto NuGet Microsoft.Extensions.Hosting. Creare una nuova applicazione console e incollarvi il contenuto del file di progetto seguente:

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

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

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

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
  </ItemGroup>

</Project>

Il file di progetto precedente definisce che:

  • L'applicazione è eseguibile.
  • Un file appsettings.json deve essere copiato nella directory di output quando il progetto viene compilato.
  • Viene aggiunto il riferimento al pacchetto NuGet Microsoft.Extensions.Hosting.

Aggiungere il file appsettings.json alla radice del progetto con il contenuto seguente:

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

Sostituire il contenuto del file Program.cs con il codice C# seguente:

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!

Quando si esegue questa applicazione, Host.CreateApplicationBuilder definisce il comportamento per individuare la configurazione JSON ed esporla tramite l'istanza IConfiguration. Dall'istanza di host è possibile richiedere al provider di servizi l'istanza di IConfiguration e quindi richiedere i valori.

Suggerimento

L'uso in questo modo dell'istanza IConfiguration non elaborata, sebbene conveniente, non è molto scalabile. Quando le applicazioni aumentano di complessità e le configurazioni corrispondenti diventano più complesse, è consigliabile usare il modello di opzioni come alternativa.

Esempio di base con hosting e uso dell'API dell'indicizzatore

Si consideri lo stesso contenuto del file appsettings.json dell'esempio precedente:

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

Sostituire il contenuto del file Program.cs con il codice C# seguente:

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

È possibile accedere ai valori usando l'API dell'indicizzatore in cui ogni chiave è una stringa e il valore è una stringa. La configurazione supporta proprietà, oggetti, matrici e dizionari.

Provider di configurazione

La tabella seguente mostra i provider di configurazione disponibili per le app .NET Core.

Provider Fornisce la configurazione da
Provider di configurazione app di Azure Configurazione app di Azure
Azure Key Vault configuration provider (Provider di configurazione di Azure Key Vault) Azure Key Vault
Provider di configurazione della riga di comando Parametri della riga di comando
Provider di configurazione personalizzato Origine personalizzata
Provider di configurazione delle variabili di ambiente Variabili di ambiente
Provider di configurazione file File JSON, XML e INI
Provider di configurazione chiave-per-file File della directory
Provider di configurazione della memoria Raccolte in memoria
Segreti dell'app (Secret Manager) File nella directory dei profili utente

Suggerimento

L'ordine in cui vengono aggiunti i provider di configurazione è importante. Quando vengono usati più provider di configurazione e più provider specificano la stessa chiave, viene usata l'ultima aggiunta.

Per ulteriori informazioni sui vari provider di configurazione, consultare Provider di configurazione in .NET.

Vedi anche