configuration について
強く型付けされたオプション、検証、階層化されたソースでアプリ設定を管理する .NET 開発向けスキルです。シークレットの扱いと名前付きオプションも解説します。
主な機能
- 01強く型付けされたオプションのバインド
- 02起動時の設定検証
- 03階層化された設定ソース
- 04シークレット保管の指針
- 05名前付きオプション
活用シーン
- 01設定を検証済みクラスにバインド
- 02起動時に設定エラーを検出
- 03シークレットをソースコードから分離
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ codewithmukesh/ dotnet-claude-kit --skill configurationインストール後、Claude Code で/configurationを入力して読み込まれたか確認します。
詳細
構成
基本原則
- 常に Options パターンを使用する — サービス内で
IConfigurationを直接読み取らないでください。構成セクションを、検証機能を備えた厳密な型のクラスにバインドします。 - 起動時に検証する —
ValidateDataAnnotations()とValidateOnStart()を使用して、最初のリクエストを受け付ける前に構成の誤りを検出します。 - シークレットをソースに含めない — 開発環境ではユーザーシークレットを、本番環境では Azure Key Vault または環境変数を使用します。シークレットを git にコミットしないでください。
- 構成の優先順位 —
appsettings.json→appsettings.{Environment}.json→ 環境変数 → ユーザーシークレット。後のソースが前のソースを上書きします。
パターン
Options パターン
// 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 invalid
// appsettings.json
{
"Database": {
"ConnectionString": "",
"MaxRetryCount": 3,
"CommandTimeoutSeconds": 30
}
}
Options の注入
// 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
}
}
カスタム検証(複雑なルール)
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();
Azure Key Vault(本番環境)
// 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());
}
複数の環境向けの構成
// 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");
// ...
}
}
アンチパターン
IConfiguration を直接読み取らない
// 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;
}
}
シークレットを appsettings.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)
起動時の検証を省略しない
// 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();
判断ガイド
| シナリオ | 推奨事項 |
|---|---|
| クラスへの構成のバインド | BindConfiguration を使用した Options パターン |
| シンプルで変更されない構成 | IOptions<T> |
| リクエストごとに変わる構成 | IOptionsSnapshot<T> |
| 構成を監視するバックグラウンドサービス | IOptionsMonitor<T> |
| 開発環境のシークレット | dotnet user-secrets |
| 本番環境のシークレット | Azure Key Vault または環境変数 |
| 構成の検証 | ValidateDataAnnotations() + ValidateOnStart() |
| 同じ型の複数の構成 | IOptionsSnapshot<T>.Get(name) を使用した名前付き Options |
コメント
Markdown 対応