Reading configuration with strings scattered around the code leads to typos discovered only in production. The Options pattern turns a configuration section into a typed, validated class.
The settings class
public class SmtpOptions
{
[Required] public string Host { get; set; } = "";
[Range(1, 65535)] public int Port { get; set; } = 587;
[Required, EmailAddress] public string Sender { get; set; } = "";
}
{
"Smtp": { "Host": "smtp.company.com", "Port": 587, "Sender": "noreply@company.com" }
}
Registration with validation at startup
builder.Services.AddOptions<SmtpOptions>()
.BindConfiguration("Smtp")
.ValidateDataAnnotations()
.ValidateOnStart();
With ValidateOnStart the application does not start if the configuration is wrong: a clear error at deploy time beats an unsent email at three in the morning.
IOptions, IOptionsSnapshot or IOptionsMonitor?
- IOptions<T>: values read once, ideal for settings that do not change.
- IOptionsSnapshot<T>: recomputed on every request, useful if the configuration file can change at runtime.
- IOptionsMonitor<T>: current value plus change notifications, suited to singletons.
Secrets
Passwords and keys do not belong in the configuration file saved in the repository: in development use user secrets, in production environment variables or a secrets store. The Options pattern does not change: it reads from all these sources in the same way.
Comments (0)
No comments yet.