본문으로 건너뛰기
cccskills
무료GitHub에 공개

microsoft-extensions-configuration

Microsoft.Extensions.Options patterns including IValidateOptions, strongly-typed settings, validation on startup, and the Options pattern for clean configuration management.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md9.4 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Microsoft.Extensions Configuration Patterns

When to Use This Skill

Use this skill when:

  • Binding configuration from appsettings.json to strongly-typed classes
  • Validating configuration at application startup (fail fast)
  • Implementing complex validation logic for settings
  • Designing configuration classes that are testable and maintainable
  • Understanding IOptions<T>, IOptionsSnapshot<T>, and IOptionsMonitor<T>

Reference Files

  • advanced-patterns.md: Validators with dependencies, named options, complete production example (AkkaSettings), and testing validators

Why Configuration Validation Matters

The Problem: Applications often fail at runtime due to misconfiguration - missing connection strings, invalid URLs, out-of-range values. These failures happen deep in business logic, far from where configuration is loaded.

The Solution: Validate configuration at startup. If invalid, fail immediately with a clear error message.

// BAD: Fails at runtime when someone tries to use the service
public class EmailService
{
    public EmailService(IOptions<SmtpSettings> options)
    {
        var settings = options.Value;
        // Throws NullReferenceException 10 minutes into production
        _client = new SmtpClient(settings.Host, settings.Port);
    }
}

// GOOD: Fails at startup with clear error
// "SmtpSettings validation failed: Host is required"

Pattern 1: Basic Options Binding

Define a Settings Class

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;
}

Bind from Configuration

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

// appsettings.json
{
  "Smtp": {
    "Host": "smtp.example.com",
    "Port": 587,
    "Username": "user@example.com",
    "Password": "secret",
    "UseSsl": true
  }
}

Consume in Services

public class EmailService
{
    private readonly SmtpSettings _settings;

    // IOptions<T> - singleton, read once at startup
    public EmailService(IOptions<SmtpSettings> options)
    {
        _settings = options.Value;
    }
}

Pattern 2: Data Annotations Validation

For simple validation rules, use Data Annotations:

using System.ComponentModel.DataAnnotations;

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

    [Required(ErrorMessage = "SMTP host is required")]
    public string Host { get; set; } = string.Empty;

    [Range(1, 65535, ErrorMessage = "Port must be between 1 and 65535")]
    public int Port { get; set; } = 587;

    [EmailAddress(ErrorMessage = "Username must be a valid email address")]
    public string? Username { get; set; }

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

Enable Data Annotations Validation

builder.Services.AddOptions<SmtpSettings>()
    .BindConfiguration(SmtpSettings.SectionName)
    .ValidateDataAnnotations()  // Enable attribute-based validation
    .ValidateOnStart();         // Validate immediately at startup

Key Point: .ValidateOnStart() is critical. Without it, validation only runs when the options are first accessed.


Pattern 3: IValidateOptions<T> for Complex Validation

Data Annotations work for simple rules, but complex validation requires IValidateOptions<T>:

ScenarioData AnnotationsIValidateOptions
Required fieldYesYes
Range checkYesYes
Cross-property validationNoYes
Conditional validationNoYes
External service checksNoYes
Dependency injection in validatorNoYes

Implementing 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 is required");

        if (options.Port is < 1 or > 65535)
            failures.Add($"Port {options.Port} is invalid. Must be between 1 and 65535");

        // Cross-property validation
        if (!string.IsNullOrEmpty(options.Username) && string.IsNullOrEmpty(options.Password))
            failures.Add("Password is required when Username is specified");

        // Conditional validation
        if (options.UseSsl && options.Port == 25)
            failures.Add("Port 25 is typically not used with SSL. Consider port 465 or 587");

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

Register the Validator

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

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

Order matters: Data Annotations run first, then IValidateOptions validators. All failures are collected together.

See advanced-patterns.md for validators with dependencies, named options, and a complete production example.


Pattern 4: Options Lifetime

InterfaceLifetimeReloads on ChangeUse Case
IOptions<T>SingletonNoStatic config, read once
IOptionsSnapshot<T>ScopedYes (per request)Web apps needing fresh config
IOptionsMonitor<T>SingletonYes (with callback)Background services, real-time updates

IOptionsMonitor for Background Services

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);
        }
    }
}

Pattern 5: Post-Configuration

Modify options after binding but before validation:

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();

Anti-Patterns to Avoid

1. Manual Configuration Access

// BAD: Bypasses validation, hard to test
public class MyService
{
    public MyService(IConfiguration configuration)
    {
        var host = configuration["Smtp:Host"]; // No validation!
    }
}

// GOOD: Strongly-typed, validated
public class MyService
{
    public MyService(IOptions<SmtpSettings> options)
    {
        var host = options.Value.Host; // Validated at startup
    }
}

2. Validation in Constructor

// BAD: Validation happens at runtime, not startup
public class MyService
{
    public MyService(IOptions<Settings> options)
    {
        if (string.IsNullOrEmpty(options.Value.Required))
            throw new ArgumentException("Required is missing"); // Too late!
    }
}

// GOOD: Validation at startup via IValidateOptions + ValidateOnStart()

3. Forgetting ValidateOnStart

// BAD: Validation only runs when first accessed
builder.Services.AddOptions<Settings>()
    .ValidateDataAnnotations(); // Missing ValidateOnStart!

// GOOD: Fails immediately if invalid
builder.Services.AddOptions<Settings>()
    .ValidateDataAnnotations()
    .ValidateOnStart();

4. Throwing in IValidateOptions

// BAD: Throws exception, breaks validation chain
public ValidateOptionsResult Validate(string? name, Settings options)
{
    if (options.Value < 0)
        throw new ArgumentException("Value cannot be negative"); // Wrong!
    return ValidateOptionsResult.Success;
}

// GOOD: Return failure result
public ValidateOptionsResult Validate(string? name, Settings options)
{
    if (options.Value < 0)
        return ValidateOptionsResult.Fail("Value cannot be negative");
    return ValidateOptionsResult.Success;
}

Summary

PrincipleImplementation
Fail fast.ValidateOnStart()
Strongly-typedBind to POCO classes
Simple validationData Annotations
Complex validationIValidateOptions<T>
Cross-property rulesIValidateOptions<T>
Environment-awareInject IHostEnvironment
TestableValidators are plain classes

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

When the user wants to plan, design, or implement an A/B test or experiment. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," or "hypothesis." For tracking implementation, see analytics-tracking.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ComeOnOliver/skillshub642026년 6월 24일 업데이트

Audit and improve web accessibility following WCAG 2.1 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ComeOnOliver/skillshub642026년 6월 24일 업데이트

Audit and improve web accessibility following WCAG 2.2 guidelines. Use when asked to "improve accessibility", "a11y audit", "WCAG compliance", "screen reader support", "keyboard navigation", or "make accessible".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ComeOnOliver/skillshub642026년 6월 24일 업데이트

This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ComeOnOliver/skillshub642026년 6월 24일 업데이트

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ComeOnOliver/skillshub642026년 6월 24일 업데이트

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ComeOnOliver/skillshub642026년 6월 24일 업데이트

ComeOnOliver의 스킬 모두 보기

이 스킬의 문제 신고