configuration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseConfiguration
配置
Core Principles
核心原则
- Options pattern always — Never read directly in services. Bind configuration sections to strongly-typed classes with validation.
IConfiguration - Validate on startup — Use and
ValidateDataAnnotations()to catch misconfiguration before the first request.ValidateOnStart() - Secrets never in source — Use user secrets in development, Azure Key Vault or environment variables in production. Never commit secrets to git.
- Configuration layering — →
appsettings.json→ environment variables → user secrets. Later sources override earlier ones.appsettings.{Environment}.json
- 始终使用Options模式 — 绝不在服务中直接读取。将配置段绑定到带验证的强类型类。
IConfiguration - 启动时验证 — 使用和
ValidateDataAnnotations()在首次请求前捕获配置错误。ValidateOnStart() - 密钥永不存入源码 — 开发环境使用用户密钥,生产环境使用Azure Key Vault或环境变量。绝不将密钥提交到git。
- 配置分层 — →
appsettings.json→ 环境变量 → 用户密钥。后续来源会覆盖之前的来源。appsettings.{Environment}.json
Patterns
模式
Options Pattern
Options模式
csharp
// Options class with validation attributes
public class DatabaseOptions
{
public const string SectionName = "Database";
[Required]
public required string ConnectionString { get; init; }
[Range(1, 100)]
public int MaxRetryCount { get; init; } = 3;
[Range(1, 60)]
public int CommandTimeoutSeconds { get; init; } = 30;
}
// Registration with validation
builder.Services.AddOptions<DatabaseOptions>()
.BindConfiguration(DatabaseOptions.SectionName)
.ValidateDataAnnotations()
.ValidateOnStart(); // Fails at startup if configuration is invalidjson
// appsettings.json
{
"Database": {
"ConnectionString": "",
"MaxRetryCount": 3,
"CommandTimeoutSeconds": 30
}
}csharp
// 带验证属性的Options类
public class DatabaseOptions
{
public const string SectionName = "Database";
[Required]
public required string ConnectionString { get; init; }
[Range(1, 100)]
public int MaxRetryCount { get; init; } = 3;
[Range(1, 60)]
public int CommandTimeoutSeconds { get; init; } = 30;
}
// 带验证的注册
builder.Services.AddOptions<DatabaseOptions>()
.BindConfiguration(DatabaseOptions.SectionName)
.ValidateDataAnnotations()
.ValidateOnStart(); // 若配置无效,启动时直接失败json
// appsettings.json
{
"Database": {
"ConnectionString": "",
"MaxRetryCount": 3,
"CommandTimeoutSeconds": 30
}
}Injecting Options
注入Options
csharp
// IOptions<T> — singleton, read once at startup, doesn't change
public class OrderService(IOptions<DatabaseOptions> options)
{
private readonly DatabaseOptions _db = options.Value;
}
// IOptionsSnapshot<T> — scoped, re-reads per request (for reloadable config)
public class OrderService(IOptionsSnapshot<DatabaseOptions> options)
{
private readonly DatabaseOptions _db = options.Value;
}
// IOptionsMonitor<T> — singleton, actively watches for changes
public class BackgroundWorker(IOptionsMonitor<WorkerOptions> options)
{
public void DoWork()
{
var current = options.CurrentValue; // Always latest
}
}csharp
// IOptions<T> — 单例,启动时读取一次,不会变更
public class OrderService(IOptions<DatabaseOptions> options)
{
private readonly DatabaseOptions _db = options.Value;
}
// IOptionsSnapshot<T> — 作用域,每个请求重新读取(适用于可重载配置)
public class OrderService(IOptionsSnapshot<DatabaseOptions> options)
{
private readonly DatabaseOptions _db = options.Value;
}
// IOptionsMonitor<T> — 单例,主动监听变更
public class BackgroundWorker(IOptionsMonitor<WorkerOptions> options)
{
public void DoWork()
{
var current = options.CurrentValue; // 始终获取最新值
}
}Custom Validation (Complex Rules)
自定义验证(复杂规则)
csharp
builder.Services.AddOptions<JwtOptions>()
.BindConfiguration("Jwt")
.Validate(options =>
{
if (string.IsNullOrEmpty(options.Key) || options.Key.Length < 32)
return false;
if (options.ExpirationMinutes <= 0)
return false;
return true;
}, "JWT key must be at least 32 characters and expiration must be positive")
.ValidateOnStart();csharp
builder.Services.AddOptions<JwtOptions>()
.BindConfiguration("Jwt")
.Validate(options =>
{
if (string.IsNullOrEmpty(options.Key) || options.Key.Length < 32)
return false;
if (options.ExpirationMinutes <= 0)
return false;
return true;
}, "JWT密钥长度至少为32字符,且过期时间必须为正数")
.ValidateOnStart();Azure Key Vault (Production)
Azure Key Vault(生产环境)
csharp
// Program.cs — add Key Vault as a configuration source
if (builder.Environment.IsProduction())
{
var keyVaultUri = new Uri(builder.Configuration["KeyVault:Uri"]!);
builder.Configuration.AddAzureKeyVault(keyVaultUri, new DefaultAzureCredential());
}csharp
// Program.cs — 添加Key Vault作为配置源
if (builder.Environment.IsProduction())
{
var keyVaultUri = new Uri(builder.Configuration["KeyVault:Uri"]!);
builder.Configuration.AddAzureKeyVault(keyVaultUri, new DefaultAzureCredential());
}Configuration for Multiple Environments
多环境配置
csharp
// Named options — different config per named instance
builder.Services.AddOptions<SmtpOptions>("internal")
.BindConfiguration("Smtp:Internal");
builder.Services.AddOptions<SmtpOptions>("customer")
.BindConfiguration("Smtp:Customer");
// Usage
public class EmailService(IOptionsSnapshot<SmtpOptions> options)
{
public async Task SendInternalEmail(string to, string body)
{
var smtp = options.Get("internal");
// ...
}
}csharp
// 命名Options — 不同命名实例对应不同配置
builder.Services.AddOptions<SmtpOptions>("internal")
.BindConfiguration("Smtp:Internal");
builder.Services.AddOptions<SmtpOptions>("customer")
.BindConfiguration("Smtp:Customer");
// 使用方式
public class EmailService(IOptionsSnapshot<SmtpOptions> options)
{
public async Task SendInternalEmail(string to, string body)
{
var smtp = options.Get("internal");
// ...
}
}Anti-patterns
反模式
Don't Read IConfiguration Directly
不要直接读取IConfiguration
csharp
// BAD — stringly-typed, no validation, hard to test
public class OrderService(IConfiguration config)
{
public void Process()
{
var timeout = int.Parse(config["Database:CommandTimeout"]!);
}
}
// GOOD — strongly-typed options
public class OrderService(IOptions<DatabaseOptions> options)
{
public void Process()
{
var timeout = options.Value.CommandTimeoutSeconds;
}
}csharp
// 错误示例 — 字符串类型、无验证、难以测试
public class OrderService(IConfiguration config)
{
public void Process()
{
var timeout = int.Parse(config["Database:CommandTimeout"]!);
}
}
// 正确示例 — 强类型Options
public class OrderService(IOptions<DatabaseOptions> options)
{
public void Process()
{
var timeout = options.Value.CommandTimeoutSeconds;
}
}Don't Put Secrets in appsettings.json
不要将密钥存入appsettings.json
json
// BAD — committed to source control
{
"Jwt": { "Key": "super-secret-key" },
"Database": { "ConnectionString": "Server=prod;Password=secret" }
}
// GOOD — appsettings.json has defaults/structure only
{
"Jwt": { "Key": "", "Issuer": "myapp", "Audience": "myapp" },
"Database": { "ConnectionString": "" }
}
// Secrets provided via user-secrets (dev) or env vars / Key Vault (prod)json
// 错误示例 — 会提交到版本控制
{
"Jwt": { "Key": "super-secret-key" },
"Database": { "ConnectionString": "Server=prod;Password=secret" }
}
// 正确示例 — appsettings.json仅包含默认值/结构
{
"Jwt": { "Key": "", "Issuer": "myapp", "Audience": "myapp" },
"Database": { "ConnectionString": "" }
}
// 密钥通过用户密钥(开发环境)或环境变量/Key Vault(生产环境)提供Don't Skip Startup Validation
不要跳过启动验证
csharp
// BAD — misconfiguration discovered at runtime
builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection("Jwt"));
// GOOD — fail fast at startup
builder.Services.AddOptions<JwtOptions>()
.BindConfiguration("Jwt")
.ValidateDataAnnotations()
.ValidateOnStart();csharp
// 错误示例 — 配置错误在运行时才被发现
builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection("Jwt"));
// 正确示例 — 启动时快速失败
builder.Services.AddOptions<JwtOptions>()
.BindConfiguration("Jwt")
.ValidateDataAnnotations()
.ValidateOnStart();Decision Guide
决策指南
| Scenario | Recommendation |
|---|---|
| Binding config to class | Options pattern with |
| Simple, immutable config | |
| Config that changes per request | |
| Background service watching config | |
| Development secrets | |
| Production secrets | Azure Key Vault or environment variables |
| Validating config | |
| Multiple configs of same type | Named options with |
| 场景 | 推荐方案 |
|---|---|
| 将配置绑定到类 | 使用Options模式配合 |
| 简单、不可变配置 | |
| 每个请求会变更的配置 | |
| 监听配置变更的后台服务 | |
| 开发环境密钥 | |
| 生产环境密钥 | Azure Key Vault或环境变量 |
| 配置验证 | |
| 同一类型的多套配置 | 命名Options配合 |