Communityコーディング&開発github.com

Microsoft.Extensions 構成パターン

IValidateOptions、厳密に型指定された設定、起動時の検証、クリーンな構成管理のためのOptionsパターンなど、Microsoft.Extensions.Options のパターンを提供します。

Microsoft.Extensions 構成パターン とは?

このスキルは、.NET アプリケーションにおける構成管理のための Microsoft.Extensions.Options パターンを包括的に提供します。appsettings.json などの構成ソースから厳密に型指定されたクラスへの設定のバインド、起動時の即時検証による失敗の早期発見、データ注釈や IValidateOptions<T> を使用した複雑な検証ロジックの実装方法をカバーします。IOptions<T>、IOptionsSnapshot<T>、IOptionsMonitor<T> の各インターフェースのライフサイクルと使用シーンを明確に区別し、バックグラウンドサービスでの設定変更の動的反映や、PostConfigure による事後処理もサポートします。主に Claude Code, Cursor, Codex などの AI エージェントが .NET プロジェクトで構成管理コードを生成・修正する際に役立ちます。また、手動での IConfiguration アクセスやコンストラクタ内検証、ValidateOnStart の欠落といったアンチパターンを回避するためのガイダンスも含まれています。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/microsoft-extensions-configuration

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Microsoft.Extensions 構成パターン は何をしますか?

このスキルを使用するタイミング

以下の場合にこのスキルを使用します:

  • appsettings.json から厳密に型指定されたクラスへの構成のバインド
  • アプリケーション起動時の構成検証(フェイルファスト)
  • 設定に対する複雑な検証ロジックの実装
  • テスト可能で保守可能な構成クラスの設計
  • IOptions、IOptionsSnapshot、IOptionsMonitor の理解

参考ファイル

  • advanced-patterns.md:依存関係を持つバリデーター、名前付きオプション、完全なプロダクション例(AkkaSettings)、バリデーターのテスト

構成検証が重要な理由

問題点: アプリケーションは、構成ミス(接続文字列の欠落、無効なURL、範囲外の値)により実行時に頻繁に失敗します。これらの失敗は、構成が読み込まれた場所から遠く離れたビジネスロジックの深部で発生します。

解決策: 起動時に構成を検証します。無効な場合は、明確なエラーメッセージとともに即座に失敗します。

// 悪い例:誰かがサービスを使用しようとしたときに実行時に失敗する
public class EmailService
{
    public EmailService(IOptions<SmtpSettings> options)
    {
        var settings = options.Value;
        // プロダクション環境で10分後に NullReferenceException をスロー
        _client = new SmtpClient(settings.Host, settings.Port);
    }
}

// 良い例:起動時に明確なエラーで失敗する
// "SmtpSettings の検証に失敗しました: Host は必須です"

パターン 1:基本的なオプションバインディング

設定クラスの定義

public class SmtpSettings
{
    public const string SectionName = "Smtp";

    public string Host { get; set; } = string.Empty;
    public int Port { get; set; } = 587;
    public string? Username { get; set; }
    public string? Password { get; set; }
    public bool UseSsl { get; set; } = true;
}

構成からのバインド

builder.Services.AddOptions<SmtpSettings>()
    .BindConfiguration(SmtpSettings.SectionName);

// appsettings.json
{
  "Smtp": {
    "Host": "smtp.example.com",
    "Port": 587,
    "Username": "[email protected]",
    "Password": "secret",
    "UseSsl": true
  }
}

サービスでの使用

public class EmailService
{
    private readonly SmtpSettings _settings;

    // IOptions<T> - シングルトン、起動時に一度だけ読み取り
    public EmailService(IOptions<SmtpSettings> options)
    {
        _settings = options.Value;
    }
}

パターン 2:データ注釈による検証

単純な検証ルールには、データ注釈を使用します:

using System.ComponentModel.DataAnnotations;

public class SmtpSettings
{
    public const string SectionName = "Smtp";

    [Required(ErrorMessage = "SMTP ホストは必須です")]
    public string Host { get; set; } = string.Empty;

    [Range(1, 65535, ErrorMessage = "ポートは 1 から 65535 の間でなければなりません")]
    public int Port { get; set; } = 587;

    [EmailAddress(ErrorMessage = "ユーザー名は有効なメールアドレスである必要があります")]
    public string? Username { get; set; }

    public string? Password { get; set; }
    public bool UseSsl { get; set; } = true;
}

データ注釈検証の有効化

builder.Services.AddOptions<SmtpSettings>()
    .BindConfiguration(SmtpSettings.SectionName)
    .ValidateDataAnnotations()  // 属性ベースの検証を有効化
    .ValidateOnStart();         // 起動時に即座に検証

重要なポイント: .ValidateOnStart() は重要です。これがないと、検証はオプションが最初にアクセスされたときのみ実行されます。


パターン 3:複雑な検証のための IValidateOptions

データ注釈は単純なルールに有効ですが、複雑な検証には IValidateOptions<T> が必要です:

シナリオデータ注釈IValidateOptions
必須フィールドはいはい
範囲チェックはいはい
プロパティ間検証いいえはい
条件付き検証いいえはい
外部サービスチェックいいえはい
バリデーターへの依存性注入いいえはい

IValidateOptions の実装

using Microsoft.Extensions.Options;

public class SmtpSettingsValidator : IValidateOptions<SmtpSettings>
{
    public ValidateOptionsResult Validate(string? name, SmtpSettings options)
    {
        var failures = new List<string>();

        if (string.IsNullOrWhiteSpace(options.Host))
            failures.Add("Host は必須です");

        if (options.Port is < 1 or > 65535)
            failures.Add($"Port {options.Port} は無効です。1 から 65535 の間である必要があります");

        // プロパティ間検証
        if (!string.IsNullOrEmpty(options.Username) && string.IsNullOrEmpty(options.Password))
            failures.Add("ユーザー名が指定されている場合はパスワードが必要です");

        // 条件付き検証
        if (options.UseSsl && options.Port == 25)
            failures.Add("ポート 25 は通常 SSL では使用されません。ポート 465 または 587 を検討してください");

        return failures.Count > 0
            ? ValidateOptionsResult.Fail(failures)
            : ValidateOptionsResult.Success;
    }
}

バリデーターの登録

builder.Services.AddOptions<SmtpSettings>()
    .BindConfiguration(SmtpSettings.SectionName)
    .ValidateDataAnnotations()
    .ValidateOnStart();

builder.Services.AddSingleton<IValidateOptions<SmtpSettings>, SmtpSettingsValidator>();

順序が重要: データ注釈が最初に実行され、次に IValidateOptions バリデーターが実行されます。すべての失敗がまとめて収集されます。

依存関係を持つバリデーター、名前付きオプション、完全なプロダクション例については、advanced-patterns.md を参照してください。


パターン 4:オプションのライフタイム

インターフェースライフタイム変更時の再読み込みユースケース
IOptions<T>シングルトンいいえ静的構成、一度だけ読み取り
IOptionsSnapshot<T>スコープはい(リクエストごと)最新の構成が必要なWebアプリ
IOptionsMonitor<T>シングルトンはい(コールバック付き)バックグラウンドサービス、リアルタイム更新

バックグラウンドサービスのための IOptionsMonitor

public class BackgroundWorker : BackgroundService
{
    private readonly IOptionsMonitor<WorkerSettings> _optionsMonitor;
    private WorkerSettings _currentSettings;

    public BackgroundWorker(IOptionsMonitor<WorkerSettings> optionsMonitor)
    {
        _optionsMonitor = optionsMonitor;
        _currentSettings = optionsMonitor.CurrentValue;

        _optionsMonitor.OnChange(settings =>
        {
            _currentSettings = settings;
        });
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await DoWorkAsync();
            await Task.Delay(_currentSettings.PollingInterval, stoppingToken);
        }
    }
}

パターン 5:ポスト構成

バインディング後、検証前にオプションを変更します:

builder.Services.AddOptions<ApiSettings>()
    .BindConfiguration("Api")
    .PostConfigure(options =>
    {
        if (!string.IsNullOrEmpty(options.BaseUrl) && !options.BaseUrl.EndsWith('/'))
            options.BaseUrl += '/';

        options.Timeout ??= TimeSpan.FromSeconds(30);
    })
    .ValidateDataAnnotations()
    .ValidateOnStart();

避けるべきアンチパターン

1. 手動による構成アクセス

// 悪い例:検証をバイパスし、テストが困難
public class MyService
{
    public MyService(IConfiguration configuration)
    {
        var host = configuration["Smtp:Host"]; // 検証なし!
    }
}

// 良い例:厳密に型指定され、検証済み
public class MyService
{
    public MyService(IOptions<SmtpSettings> options)
    {
        var host = options.Value.Host; // 起動時に検証済み
    }
}

2. コンストラクタ内での検証

// 悪い例:検証が起動時ではなく実行時に発生
public class MyService
{
    public MyService(IOptions<Settings> options)
    {
        if (string.IsNullOrEmpty(options.Value.Required))
            throw new ArgumentException("Required がありません"); // 手遅れ!
    }
}

// 良い例:IValidateOptions + ValidateOnStart による起動時検証

3. ValidateOnStart の忘れ

// 悪い例:検証は最初にアクセスされたときのみ実行
builder.Services.AddOptions<Settings>()
    .ValidateDataAnnotations(); // ValidateOnStart が欠落!

// 良い例:無効な場合に即座に失敗
builder.Services.AddOptions<Settings>()
    .ValidateDataAnnotations()
    .ValidateOnStart();

4. IValidateOptions 内での例外スロー

// 悪い例:例外をスローし、検証チェーンを壊す
public ValidateOptionsResult Validate(string? name, Settings options)
{
    if (options.Value < 0)
        throw new ArgumentException("値を負にできません"); // 間違い!
    return ValidateOptionsResult.Success;
}

// 良い例:失敗結果を返す
public ValidateOptionsResult Validate(string? name, Settings options)
{
    if (options.Value < 0)
        return ValidateOptionsResult.Fail("値を負にできません");
    return ValidateOptionsResult.Success;
}

まとめ

原則実装
フェイルファスト.ValidateOnStart()
厳密な型指定POCO クラスにバインド
単純な検証データ注釈
複雑な検証IValidateOptions<T>
プロパティ間ルールIValidateOptions<T>
環境認識IHostEnvironment の注入
テスト可能性バリデーターはプレーンなクラス

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

関連スキル