microsoft-extensions-configuration

Padrões do Microsoft.Extensions.Options, incluindo IValidateOptions, configurações fortemente tipadas, validação na inicialização e o padrão Options para gerenciamento limpo de configuração.

O que é microsoft-extensions-configuration?

Esta skill fornece um guia completo sobre como usar os padrões de configuração do Microsoft.Extensions no .NET, focando em IOptions<T>, validação de configurações e boas práticas. Ela ensina como evitar falhas em tempo de execução devido a configurações incorretas, validando-as na inicialização da aplicação (fail fast). A skill cobre desde a vinculação básica de seções do appsettings.json a classes fortemente tipadas, até validações complexas com IValidateOptions, incluindo validação cruzada de propriedades e dependências injetadas. Também aborda os diferentes ciclos de vida das opções (IOptions, IOptionsSnapshot, IOptionsMonitor) e fornece anti-padrões para evitar, como acessar o IConfiguration diretamente ou esquecer de chamar ValidateOnStart. Projetada para desenvolvedores C# que trabalham com ASP.NET Core, Azure Functions ou qualquer aplicação .NET que use o sistema de configuração padrão.

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

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Padrões de Configuração do Microsoft.Extensions

Quando Usar Esta Skill

Use esta skill quando:

  • Vincular configuração de appsettings.json para classes fortemente tipadas
  • Validar configuração na inicialização da aplicação (fail fast)
  • Implementar lógica de validação complexa para configurações
  • Projetar classes de configuração que sejam testáveis e sustentáveis
  • Compreender IOptions, IOptionsSnapshot e IOptionsMonitor

Arquivos de Referência

  • advanced-patterns.md: Validadores com dependências, opções nomeadas, exemplo completo de produção (AkkaSettings) e teste de validadores

Por que a Validação de Configuração é Importante

O Problema: Aplicações frequentemente falham em tempo de execução devido a configurações incorretas - strings de conexão ausentes, URLs inválidos, valores fora do intervalo. Essas falhas ocorrem profundamente na lógica de negócio, longe de onde a configuração é carregada.

A Solução: Validar a configuração na inicialização. Se for inválida, falhe imediatamente com uma mensagem de erro clara.

// RUIM: Falha em tempo de execução quando alguém tenta usar o serviço
public class EmailService
{
    public EmailService(IOptions<SmtpSettings> options)
    {
        var settings = options.Value;
        // Lança NullReferenceException 10 minutos após a produção
        _client = new SmtpClient(settings.Host, settings.Port);
    }
}

// BOM: Falha na inicialização com erro claro
// "Validação de SmtpSettings falhou: Host é obrigatório"

Padrão 1: Vinculação Básica de Opções

Definir uma Classe de Configurações

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

Vincular a partir da Configuração

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

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

Consumir em Serviços

public class EmailService
{
    private readonly SmtpSettings _settings;

    // IOptions<T> - singleton, lido uma vez na inicialização
    public EmailService(IOptions<SmtpSettings> options)
    {
        _settings = options.Value;
    }
}

Padrão 2: Validação com Data Annotations

Para regras de validação simples, use Data Annotations:

using System.ComponentModel.DataAnnotations;

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

    [Required(ErrorMessage = "Host SMTP é obrigatório")]
    public string Host { get; set; } = string.Empty;

    [Range(1, 65535, ErrorMessage = "Porta deve estar entre 1 e 65535")]
    public int Port { get; set; } = 587;

    [EmailAddress(ErrorMessage = "Username deve ser um endereço de e-mail válido")]
    public string? Username { get; set; }

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

Habilitar Validação com Data Annotations

builder.Services.AddOptions<SmtpSettings>()
    .BindConfiguration(SmtpSettings.SectionName)
    .ValidateDataAnnotations()  // Habilita validação baseada em atributos
    .ValidateOnStart();         // Valida imediatamente na inicialização

Ponto Chave: .ValidateOnStart() é crítico. Sem ele, a validação só é executada quando as opções são acessadas pela primeira vez.


Padrão 3: IValidateOptions para Validação Complexa

Data Annotations funcionam para regras simples, mas a validação complexa requer IValidateOptions<T>:

CenárioData AnnotationsIValidateOptions
Campo obrigatórioSimSim
Verificação de intervaloSimSim
Validação entre propriedadesNãoSim
Validação condicionalNãoSim
Verificações de serviço externoNãoSim
Injeção de dependência no validadorNãoSim

Implementando 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 é obrigatório");

        if (options.Port is < 1 or > 65535)
            failures.Add($"Porta {options.Port} é inválida. Deve estar entre 1 e 65535");

        // Validação entre propriedades
        if (!string.IsNullOrEmpty(options.Username) && string.IsNullOrEmpty(options.Password))
            failures.Add("Senha é obrigatória quando Nome de Usuário é especificado");

        // Validação condicional
        if (options.UseSsl && options.Port == 25)
            failures.Add("Porta 25 normalmente não é usada com SSL. Considere a porta 465 ou 587");

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

Registrar o Validador

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

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

A ordem importa: Data Annotations são executados primeiro, depois os validadores IValidateOptions. Todas as falhas são coletadas juntas.

Veja advanced-patterns.md para validadores com dependências, opções nomeadas e um exemplo completo de produção.


Padrão 4: Ciclo de Vida das Opções

InterfaceCiclo de VidaRecarrega com MudançasCaso de Uso
IOptions<T>SingletonNãoConfiguração estática, lida uma vez
IOptionsSnapshot<T>EscopoSim (por requisição)Aplicações web que precisam de configuração atualizada
IOptionsMonitor<T>SingletonSim (com callback)Serviços em segundo plano, atualizações em tempo real

IOptionsMonitor para Serviços em Segundo Plano

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

Padrão 5: Pós-Configuração

Modifique as opções após a vinculação, mas antes da validação:

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-Padrões a Evitar

1. Acesso Manual à Configuração

// RUIM: Ignora validação, difícil de testar
public class MyService
{
    public MyService(IConfiguration configuration)
    {
        var host = configuration["Smtp:Host"]; // Sem validação!
    }
}

// BOM: Fortemente tipado, validado
public class MyService
{
    public MyService(IOptions<SmtpSettings> options)
    {
        var host = options.Value.Host; // Validado na inicialização
    }
}

2. Validação no Construtor

// RUIM: Validação acontece em tempo de execução, não na inicialização
public class MyService
{
    public MyService(IOptions<Settings> options)
    {
        if (string.IsNullOrEmpty(options.Value.Required))
            throw new ArgumentException("Obrigatório está ausente"); // Tarde demais!
    }
}

// BOM: Validação na inicialização via IValidateOptions + ValidateOnStart()

3. Esquecer o ValidateOnStart

// RUIM: Validação só é executada quando acessada pela primeira vez
builder.Services.AddOptions<Settings>()
    .ValidateDataAnnotations(); // Faltou ValidateOnStart!

// BOM: Falha imediatamente se inválido
builder.Services.AddOptions<Settings>()
    .ValidateDataAnnotations()
    .ValidateOnStart();

4. Lançar Exceção no IValidateOptions

// RUIM: Lança exceção, quebra a cadeia de validação
public ValidateOptionsResult Validate(string? name, Settings options)
{
    if (options.Value < 0)
        throw new ArgumentException("Valor não pode ser negativo"); // Errado!
    return ValidateOptionsResult.Success;
}

// BOM: Retorna resultado de falha
public ValidateOptionsResult Validate(string? name, Settings options)
{
    if (options.Value < 0)
        return ValidateOptionsResult.Fail("Valor não pode ser negativo");
    return ValidateOptionsResult.Success;
}

Resumo

PrincípioImplementação
Falhar rápido.ValidateOnStart()
Fortemente tipadoVincular a classes POCO
Validação simplesData Annotations
Validação complexaIValidateOptions<T>
Regras entre propriedadesIValidateOptions<T>
Ciente de ambienteInjetar IHostEnvironment
TestávelValidadores são classes simples

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.

Habilidades Relacionadas