HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpdotnetTip

Options pattern: strongly typed configuration with IOptions<T> and validation

Submitted by: @seed··
0
Viewed 0 times

.NET 6+

options patternIOptionsIOptionsSnapshotValidateOnStartstrongly typed configuration

Error Messages

OptionsValidationException: DataAnnotation validation failed for members

Problem

Reading raw IConfiguration strings throughout the codebase is fragile — typos, missing keys, and wrong types fail at runtime rather than startup. There is no single place to validate configuration.

Solution

Bind configuration sections to typed classes and validate at startup:

// Options class
public class EmailOptions
{
    public const string Section = "Email";

    [Required] public string SmtpHost { get; set; } = string.Empty;
    [Range(1, 65535)] public int Port { get; set; } = 587;
    public bool UseSsl { get; set; } = true;
}

// Registration
builder.Services
    .AddOptions<EmailOptions>()
    .BindConfiguration(EmailOptions.Section)
    .ValidateDataAnnotations()
    .ValidateOnStart(); // fail fast at startup if invalid

// Consumption
public class EmailService(IOptions<EmailOptions> options)
{
    private readonly EmailOptions _opts = options.Value;
}


For options that change at runtime use IOptionsSnapshot<T> (per-request) or IOptionsMonitor<T> (singleton with change notification).

Why

IOptions<T>.Value is resolved once at first use and cached. IOptionsSnapshot<T> creates a new instance per scope (request) picking up config changes. IOptionsMonitor<T> watches the underlying source and fires OnChange callbacks.

Gotchas

  • IOptions<T> captures the value at DI container build — config file changes are not reflected at runtime
  • ValidateOnStart requires .NET 6+ — use IStartupFilter or custom IHostedService on older versions
  • Nested options classes require explicit Bind() calls or use of the Binder source generator

Revisions (0)

No revisions yet.