.NETMedium
IOptions vs IOptionsSnapshot vs IOptionsMonitor in ASP.NET Core
Three flavours, three lifetimes, three behaviours when appsettings.json changes.
| Lifetime | Reloads on file change? | Per-request fresh? | |
|---|---|---|---|
IOptions<T> | Singleton | ❌ no | ❌ |
IOptionsSnapshot<T> | Scoped | ✅ yes (read at scope creation) | ✅ |
IOptionsMonitor<T> | Singleton | ✅ yes (current value + change notifications) | n/a |
Registration
builder.Services.Configure<EmailOptions>(builder.Configuration.GetSection("Email"));
That single line wires up all three.
Use IOptions<T> when
The value is set once at boot and never changes — DB connection strings, feature constants. Cheapest and simplest.
Use IOptionsSnapshot<T> when
A long-running web request should see one consistent snapshot even if the file is edited mid-request. Common in controllers:
public class EmailController(IOptionsSnapshot<EmailOptions> opt) : ControllerBase
{
[HttpPost] public IActionResult Send() => Ok(opt.Value.Sender);
}
Use IOptionsMonitor<T> when
You're in a singleton (background service, hosted service) and need fresh values after a config reload:
public class Throttle(IOptionsMonitor<RateOpts> monitor)
{
private int _limit = monitor.CurrentValue.Limit;
public Throttle() => monitor.OnChange(o => _limit = o.Limit);
}
Common bug
Injecting IOptionsSnapshot<T> into a singleton — Compile-time green, runtime exception about scoped-into-singleton. Use IOptionsMonitor<T> instead.
Bonus — validation
builder.Services
.AddOptions<EmailOptions>()
.Bind(builder.Configuration.GetSection("Email"))
.ValidateDataAnnotations()
.ValidateOnStart(); // fail fast at boot, not at first use