IOption Pattern | C#
- IOption Pattern | C#
🔧 Interfaces de Configuração no .NET
| Interface | Lifetime (Tempo de Vida) | Atualização de Dados | Uso Ideal |
|---|---|---|---|
IOptions<T> | Singleton | Apenas leitura única na inicialização da aplicação | Quando a configuração não muda em tempo de execução e pode ser compartilhada por toda a aplicação |
IOptionsSnapshot<T> | Scoped | Recarrega 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> | Singleton | Suporte a mudanças em tempo real, via OnChange | Quando 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,ScopedouTransient). - Isso ocorre porque o
IOptions<T>,IOptionsSnapshot<T>eIOptionsMonitor<T>já são automaticamente registrados e gerenciados pelo container de injeção de dependência. - O método
AddOptions()(chamado implicitamente porConfigure<T>) registra os providers e factories necessários para resolverIOptions<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
MyConfigcom os valores da configuração; - A registra como um
IOptions<MyConfig>no container; - Garante o ciclo de vida adequado (geralmente
SingletonparaIOptions<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
ApplicationConfignão precisa estar registrada manualmente em nenhum ciclo de vida, pois oIOptionsMonitor<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
MongoOptionscomo umSingleton— oIOptions<MongoOptions>já gerencia o ciclo de vida automaticamente com base no padrão configurado.
🧾 Resumo Final
| Ponto | Explicaçã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 automaticamente | IOptions<T> → Singleton, IOptionsSnapshot<T> → Scoped, IOptionsMonitor<T> → Singleton com atualização dinâmica. |