Learn ASP.NET Core configuration: providers, the Options pattern, secret management, and validation.
ASP.NET Core configuration is built on a layered provider system: JSON files, environment variables, command-line arguments, and secret stores all contribute to a merged configuration dictionary. The Options pattern binds configuration sections to strongly typed classes, enabling validation and clean injection into services.
The configuration system layers multiple providers, each overriding the previous:
1// appsettings.json
2{
3 "AppSettings": {
4 "ApplicationName": "My API",
5 "Version": "1.0.0",
6 "MaxItemsPerPage": 100,
7 "Features": {
8 "EnableCaching": true,
9 "EnableMetrics": true
10 }
11 },
12 "ConnectionStrings": {
13 "DefaultConnection": "Server=localhost;Database=MyDb;..."
14 },
15 "Logging": {
16 "LogLevel": {
17 "Default": "Information",
18 "Microsoft.AspNetCore": "Warning"
19 }
20 }
21}
22
23// Reading configuration in Program.cs
24var builder = WebApplication.CreateBuilder(args);
25
26// Access configuration directly
27var appName = builder.Configuration["AppSettings:ApplicationName"];
28var maxItems = builder.Configuration.GetValue<int>("AppSettings:MaxItemsPerPage");
29var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
30
31// Bind to a section
32var appSettings = builder.Configuration.GetSection("AppSettings");
33var enableCaching = appSettings.GetValue<bool>("Features:EnableCaching");
34
35Console.WriteLine($"App: {appName}, MaxItems: {maxItems}");
36Console.WriteLine($"Connection: {connectionString}");
37Console.WriteLine($"Caching: {enableCaching}");The Options pattern binds configuration sections to strongly typed POCOs:
1// Configuration providers (default order - later overrides earlier)
2var builder = WebApplication.CreateBuilder(args);
3
4// Default providers are already added:
5// 1. appsettings.json
6// 2. appsettings.{Environment}.json
7// 3. User secrets (Development only)
8// 4. Environment variables
9// 5. Command-line arguments
10
11// Add custom providers
12builder.Configuration
13 .AddJsonFile("customsettings.json", optional: true, reloadOnChange: true)
14 .AddXmlFile("config.xml", optional: true)
15 .AddIniFile("config.ini", optional: true)
16 .AddInMemoryCollection(new Dictionary<string, string?>
17 {
18 ["AppSettings:RuntimeValue"] = "InMemoryValue"
19 });
20
21// Environment-specific config
22// appsettings.Development.json
23{
24 "AppSettings": {
25 "Environment": "Development",
26 "Debug": true
27 }
28}
29
30// Environment variables
31// ASPNETCORE_ENVIRONMENT=Production
32// AppSettings__ApplicationName=Production API
33// ConnectionStrings__DefaultConnection=Server=prod;...
34
35// Command-line arguments
36// dotnet run --AppSettings:Version=2.0.0
37
38// Provider hierarchy visualization
39/*
40Command Line Args (highest priority)
41 ↓
42Environment Variables
43 ↓
44User Secrets (Dev)
45 ↓
46appsettings.{Env}.json
47 ↓
48appsettings.json (lowest priority)
49*/Never commit secrets to source control. Use dotnet user-secrets for local development:
1// Strongly-typed options classes
2public class AppSettings
3{
4 public const string SectionName = "AppSettings";
5
6 public string ApplicationName { get; set; } = "";
7 public string Version { get; set; } = "";
8 public int MaxItemsPerPage { get; set; } = 50;
9 public FeatureSettings Features { get; set; } = new();
10}
11
12public class FeatureSettings
13{
14 public bool EnableCaching { get; set; }
15 public bool EnableMetrics { get; set; }
16}
17
18// Registration
19builder.Services.Configure<AppSettings>(
20 builder.Configuration.GetSection(AppSettings.SectionName));
21
22// Usage with IOptions<T>
23public class MyService
24{
25 private readonly AppSettings _settings;
26
27 public MyService(IOptions<AppSettings> options)
28 {
29 _settings = options.Value; // Snapshot at startup
30 }
31}
32
33// Usage with IOptionsSnapshot<T> - reloads on each request
34public class ScopedService
35{
36 private readonly AppSettings _settings;
37
38 public ScopedService(IOptionsSnapshot<AppSettings> options)
39 {
40 _settings = options.Value; // Current value per request
41 }
42}
43
44// Usage with IOptionsMonitor<T> - live updates
45public class SingletonService
46{
47 private readonly IOptionsMonitor<AppSettings> _monitor;
48
49 public SingletonService(IOptionsMonitor<AppSettings> monitor)
50 {
51 _monitor = monitor;
52 _monitor.OnChange(settings =>
53 {
54 Console.WriteLine($"Settings changed: {settings.ApplicationName}");
55 });
56 }
57
58 public string GetAppName() => _monitor.CurrentValue.ApplicationName;
59}Validate configuration at startup to fail fast rather than encountering missing values at runtime:
1// User Secrets (Development only)
2// Store secrets outside source control
3
4// Initialize user secrets
5// dotnet user-secrets init
6
7// Set secrets
8// dotnet user-secrets set "Database:Password" "MySecretPassword"
9// dotnet user-secrets set "ApiKeys:Stripe" "sk_test_..."
10
11// secrets.json (stored in %APPDATA%/Microsoft/UserSecrets/{id}/)
12{
13 "Database": {
14 "Password": "MySecretPassword"
15 },
16 "ApiKeys": {
17 "Stripe": "sk_test_..."
18 }
19}
20
21// Access in code
22var dbPassword = builder.Configuration["Database:Password"];
23var stripeKey = builder.Configuration["ApiKeys:Stripe"];
24
25// Production: Use Azure Key Vault
26builder.Configuration.AddAzureKeyVault(
27 new Uri("https://myvault.vault.azure.net/"),
28 new DefaultAzureCredential());
29
30// Or AWS Secrets Manager
31builder.Configuration.AddSecretsManager(options =>
32{
33 options.SecretFilter = entry => entry.Name.StartsWith("MyApp/");
34});
35
36// Never store secrets in:
37// ❌ appsettings.json (committed to source control)
38// ❌ Environment variables in docker-compose.yml
39// ❌ Hard-coded in source code
40
41// Always use:
42// ✅ User secrets (development)
43// ✅ Azure Key Vault (production)
44// ✅ AWS Secrets Manager (production)
45// ✅ HashiCorp Vault (production)Your progress is saved in this browser, on this device only.