0Pricing
C# Academy · レッスン

IOptionsによる強く型付けされたOptions

IOptions 、IOptionsSnapshot 、IOptionsMonitor を使って、構成セクションをPOCOクラスにバインドします。

「IOptionsによる強く型付けされたOptions」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。

強く型付けされたオプションを使う理由

IConfiguration["Key"]で構成を読み取ると、型のない文字列になります。Optionsパターンは構成セクションをC#クラスにマッピングし、コンパイル時の安全性、IntelliSense、検証機能を提供します。

オプションクラスの定義

JSONキーと名前が一致するプロパティを持つ単純なPOCOクラスを作成します。慣例として、構成セクションを識別するために静的なSectionName定数を追加します。

// Configuration class
public class JwtOptions
{
    public const string SectionName = "Jwt";

    public string SecretKey { get; set; } = string.Empty;
    public string Issuer    { get; set; } = string.Empty;
    public string Audience  { get; set; } = string.Empty;
    public int    ExpiryMinutes { get; set; } = 60;
}

// appsettings.json:
{
  "Jwt": {
    "SecretKey": "my-very-secret-key",
    "Issuer": "https://myapp.com",
    "Audience": "https://myapp.com/api",
    "ExpiryMinutes": 120
  }
}

オプションの登録

Configure<T>を呼び出して、構成セクションをオプションクラスにバインドします。これにより、IOptions<T>、IOptionsSnapshot<T>、IOptionsMonitor<T>がDIに登録されます。

// Registration
builder.Services.Configure<JwtOptions>(
    builder.Configuration.GetSection(JwtOptions.SectionName));

// Alternative shorthand:
builder.Services
    .AddOptions<JwtOptions>()
    .BindConfiguration(JwtOptions.SectionName);

// Consuming via IOptions<T>:
public class TokenService
{
    private readonly JwtOptions _opts;

    public TokenService(IOptions<JwtOptions> opts)
        => _opts = opts.Value;

    public string CreateToken()
        => $"Issuer={_opts.Issuer}, Exp={_opts.ExpiryMinutes}m";
}

IOptionsとIOptionsSnapshotとIOptionsMonitorの違い

有効期間と再読み込み動作が異なる3種類があります。用途に合ったものを選択してください。

// IOptions<T> — Singleton, reads config ONCE at startup
public class ApiClient(IOptions<ApiOptions> opts)
{
    private readonly ApiOptions _opts = opts.Value; // never changes
}

// IOptionsSnapshot<T> — Scoped, reloads per request
public class ReportService(IOptionsSnapshot<ReportOptions> opts)
{
    private readonly ReportOptions _opts = opts.Value; // fresh per request
}

// IOptionsMonitor<T> — Singleton, live updates + change notifications
public class FeatureService(IOptionsMonitor<FeatureFlags> monitor)
{
    public bool IsEnabled(string feature)
        => monitor.CurrentValue.EnabledFeatures.Contains(feature);
}

属性によるオプションの検証

オプションのプロパティにSystem.ComponentModel.DataAnnotations属性を付け、ValidateDataAnnotations()を呼び出すと、構成が無効な場合に起動時に即座に失敗させることができます。

using System.ComponentModel.DataAnnotations;

public class SmtpOptions
{
    [Required]
    public string Host { get; set; } = string.Empty;

    [Range(1, 65535)]
    public int Port { get; set; } = 587;

    [Required, EmailAddress]
    public string FromAddress { get; set; } = string.Empty;
}

// Register with validation:
builder.Services
    .AddOptions<SmtpOptions>()
    .BindConfiguration("Smtp")
    .ValidateDataAnnotations()
    .ValidateOnStart(); // fail at startup, not first use

IValidateOptionsによるカスタム検証

複数のプロパティにまたがる複雑なルールには、IValidateOptions<T>を実装して、プログラムによる完全な検証ロジックを記述します。

public class JwtOptionsValidator : IValidateOptions<JwtOptions>
{
    public ValidateOptionsResult Validate(string? name, JwtOptions opts)
    {
        var errors = new List<string>();

        if (string.IsNullOrWhiteSpace(opts.SecretKey))
            errors.Add("SecretKey must not be empty");

        if (opts.SecretKey.Length < 32)
            errors.Add("SecretKey must be at least 32 characters");

        if (opts.ExpiryMinutes <= 0)
            errors.Add("ExpiryMinutes must be positive");

        return errors.Any()
            ? ValidateOptionsResult.Fail(errors)
            : ValidateOptionsResult.Success;
    }
}

builder.Services.AddSingleton<IValidateOptions<JwtOptions>, JwtOptionsValidator>();

Post-Configure

PostConfigureはすべてのConfigure呼び出しの後に実行され、値の上書きや導出を可能にします。計算プロパティや環境固有の調整に便利です。

builder.Services.Configure<CacheOptions>(
    builder.Configuration.GetSection("Cache"));

// Override in test environment:
builder.Services.PostConfigure<CacheOptions>(opts =>
{
    if (builder.Environment.IsEnvironment("Testing"))
    {
        opts.AbsoluteExpirationMinutes = 1; // very short in tests
        opts.UseDistributedCache = false;   // use in-memory cache
    }
});

// PostConfigure always runs LAST, even after AddOptions validators

名前付きオプション

同じオプション型の複数のインスタンスが必要な場合(たとえば2台のSMTPサーバー)、名前付きオプションを使用してそれぞれを区別します。

// Register named options:
builder.Services.Configure<SmtpOptions>("Primary",
    builder.Configuration.GetSection("Smtp:Primary"));
builder.Services.Configure<SmtpOptions>("Backup",
    builder.Configuration.GetSection("Smtp:Backup"));

// Consume with IOptionsMonitor (supports named options):
public class EmailSender
{
    private readonly SmtpOptions _primary;
    private readonly SmtpOptions _backup;

    public EmailSender(IOptionsMonitor<SmtpOptions> monitor)
    {
        _primary = monitor.Get("Primary");
        _backup  = monitor.Get("Backup");
    }
}

BindConfigurationの省略記法

AddOptions().BindConfiguration()のチェーンは、登録、バインド、検証、起動時の即時失敗を1つの式で行える、現代的で流暢な方法です。

// Full registration chain:
builder.Services
    .AddOptions<DatabaseOptions>()
    .BindConfiguration("Database")          // bind section
    .ValidateDataAnnotations()               // attribute validation
    .Validate(opts =>                        // custom rule
        opts.MaxPoolSize >= opts.MinPoolSize,
        "MaxPoolSize must be >= MinPoolSize")
    .ValidateOnStart();                      // fail at startup

// Shorthand for simple cases:
builder.Services.AddOptions<AppOptions>()
    .BindConfiguration(AppOptions.SectionName)
    .ValidateOnStart();

実例:機能フラグのオプション

再読み込みをサポートする完全な機能フラグ用オプションの構成例です。再デプロイせずにappsettingsで切り替えを変更できます。

public class FeatureFlags
{
    public bool EnableNewCheckout  { get; set; }
    public bool EnableAISearch     { get; set; }
    public bool EnableBetaDashboard { get; set; }
}

// appsettings.json:
// { "FeatureFlags": { "EnableNewCheckout": true, ... } }

builder.Services
    .AddOptions<FeatureFlags>()
    .BindConfiguration("FeatureFlags")
    .ValidateOnStart();

// In a controller or service:
public class CheckoutController : ControllerBase
{
    private readonly FeatureFlags _flags;

    public CheckoutController(IOptionsMonitor<FeatureFlags> m)
        => _flags = m.CurrentValue;

    [HttpGet("/checkout")]
    public IActionResult Index() =>
        _flags.EnableNewCheckout
            ? Ok("new checkout")
            : Ok("legacy checkout");
}

クイックチェック

ライブ構成の変更を反映する必要があるシングルトンサービスでは、どのIOptionsバリアントを使用すべきですか?

まとめ:IOptionsによる強く型付けされたオプション

重要なポイント:

  • Optionsパターンは、Configure<T>またはAddOptions<T>().BindConfiguration()を介して構成セクションをPOCOにバインドします
  • IOptions<T>:シングルトンで、起動時に一度だけ読み取ります
  • IOptionsSnapshot<T>:スコープ付きで、リクエストごとに再読み込みします — シングルトンには注入しないでください
  • IOptionsMonitor<T>:シングルトンから安全に使用でき、ライブのCurrentValueとOnChangeを提供します
  • DataAnnotations、ValidateDataAnnotations()、ValidateOnStart()で検証します
  • 同じ型の複数インスタンスには名前付きオプションを使用します

よくある質問

「IOptionsによる強く型付けされたOptions」レッスンは無料ですか?

はい。「IOptionsによる強く型付けされたOptions」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。

「IOptionsによる強く型付けされたOptions」で何を学びますか?

IOptions 、IOptionsSnapshot 、IOptionsMonitor を使って、構成セクションをPOCOクラスにバインドします。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C# Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「IOptionsによる強く型付けされたOptions」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC# Academyレッスンでコードを書いて実行できますか?

はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 構成ソースとプロバイダー
  2. IOptionsによる強く型付けされたOptions
  3. Optionsの検証と名前付きOptions
  4. シークレット管理
← C# Academyに戻る