Skip to main content

IOption Pattern | C#

  • IOption Pattern | C#

🔧 Interfaces de Configuração no .NET

InterfaceLifetime (Tempo de Vida)Atualização de DadosUso Ideal
IOptions<T>SingletonApenas leitura única na inicialização da aplicaçãoQuando a configuração não muda em tempo de execução e pode ser compartilhada por toda a aplicação
IOptionsSnapshot<T>ScopedRecarrega os dados a cada requisição (ideal para apps web)Quando diferentes requisições precisam acessar valores atualizados, mas não há necessidade de detecção em tempo real
IOptionsMonitor<T>SingletonSuporte a mudanças em tempo real, via OnChangeQuando você precisa detectar alterações dinâmicas em configurações (ex: monitoramento de arquivos de configuração em tempo real)

🧠 Considerações Importantes sobre o Ciclo de Vida

  • Ao utilizar o padrão Options, não é necessário registrar a classe de configuração (T) manualmente como um serviço (Singleton, Scoped ou Transient).
  • Isso ocorre porque o IOptions<T>, IOptionsSnapshot<T> e IOptionsMonitor<T> já são automaticamente registrados e gerenciados pelo container de injeção de dependência.
  • O método AddOptions() (chamado implicitamente por Configure<T>) registra os providers e factories necessários para resolver IOptions<T> sem precisar criar bindings adicionais.

Exemplo do comportamento interno:

services.Configure<MyConfig>(Configuration.GetSection("MyConfig"));

Esse código já:

  • Cria internamente a instância de MyConfig com os valores da configuração;
  • A registra como um IOptions<MyConfig> no container;
  • Garante o ciclo de vida adequado (geralmente Singleton para IOptions<T>).

👉 Portanto, não é necessário fazer algo como:

services.AddSingleton<MyConfig>();

ou

services.AddScoped<MyConfig>();

Fazer isso pode até causar comportamentos inesperados, pois o objeto criado manualmente não será o mesmo que o IOptions<T> está gerenciando.


🧩 Exemplo Prático

Classe de Configuração

public class ApplicationConfig
{
public string Name { get; set; }
}

appsettings.json

"ApplicationConfig": {
"Name": "MinhaAplicacao"
}

Registro no Program.cs

services.Configure<ApplicationConfig>(Configuration.GetSection("ApplicationConfig"));

Uso com IOptionsMonitor<T>

public class MyService
{
private readonly IOptionsMonitor<ApplicationConfig> _config;

public MyService(IOptionsMonitor<ApplicationConfig> config)
{
_config = config;
_config.OnChange(newConfig =>
{
Console.WriteLine($"Novo nome da aplicação: {newConfig.Name}");
});
}
}

⚙️ Note que ApplicationConfig não precisa estar registrada manualmente em nenhum ciclo de vida, pois o IOptionsMonitor<ApplicationConfig> faz essa injeção automaticamente.


✅ Validação com IOptions

{
"Mongo": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "bankAccount"
}
}
public class MongoOptions
{
public static string Section = "Mongo";

[Required]
public string ConnectionString { get; init; } = string.Empty;

[Required]
[MinLength(10)]
public string DatabaseName { get; init; } = string.Empty;
}
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOptions<MongoOptions>()
.BindConfiguration(MongoOptions.Section)
.ValidateDataAnnotations()
.ValidateOnStart();
public class MyService(IOptions<MongoOptions> options)
{
// ...
}

💡 Assim como no exemplo anterior, não há necessidade de adicionar MongoOptions como um Singleton — o IOptions<MongoOptions> já gerencia o ciclo de vida automaticamente com base no padrão configurado.


🧾 Resumo Final

PontoExplicação
❌ Não registrar manualmente classes de configuração (AddSingleton, etc.)O sistema de IOptions já injeta e mantém as instâncias necessárias.
✅ Utilizar Configure<T>() ou AddOptions<T>().Bind()Essa é a forma correta de vincular seções de configuração.
🔁 O ciclo de vida é gerenciado automaticamenteIOptions<T> → Singleton, IOptionsSnapshot<T> → Scoped, IOptionsMonitor<T> → Singleton com atualização dinâmica.