A few days ago I saw an interesting question where someone was asking what's
the preferred way to read some configuration, something like below.
{
"SomeSetting": {
"Config": "Value"
}
}
Would you use,
1. IConfiguration
app.MapGet("/config", (IConfiguration configuration) =>
{
return configuration.GetValue<string>("SomeSetting:Config");
});
2. Options Pattern
builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("SomeSetting"));
app.MapGet("/config", (IOptions<MyOptions> optionsAccessor) =>
{
return optionsAccessor.Value.Config;
});
public record MyOptions
{
public string Config { get; set; }
}
And that kind of prompted me to write this post.
As we all know, the more magic strings we maintain, the more trouble we ask
for. But personally, if it's something that's hardly going to change, but
still warrants being maintained as a configuration and does not use more than
a couple of magic strings, I'd go for IConfiguration.
Saying that there are a lot of benefits to using
Options Pattern.
It uses strongly typed access and reduces the use of magic strings. We can
also do
Options Validation
using Data Annotations, and we can even ask for early validation so that at
the application start, we will know if something is missing or incorrectly
configured rather than knowing it later when the app is.
On top of those, there are different Options Interfaces that can be
really handy.
-
It's a Singleton and CAN be injected into
any service lifetime.
-
Supports
Named Options.
-
Does not support reloadable changes. For new configuration values to be
reflected, the app DOES need to be restarted.
- Usage:
builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("SomeSetting"));
..
app.MapGet("/config", (IOptions<MyOptions> optionsAccessor) =>
{
return optionsAccessor.Value.Config;
});
-
It's Scoped and CAN NOT be injected into a
Singleton
service.
-
Supports
Named Options.
-
Supports reloadable changes. For new configuration values to be
reflected, the app DOES NOT need to be restarted. It's
recomputed on every request and cached for the lifetime of the
request.
- Usage:
builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("SomeSetting"));
..
app.MapGet("/config", (IOptionsSnapshot<MyOptions> snapshotOptionsAccessor) =>
{
return snapshotOptionsAccessor.Value.Config;
});
-
It's a Singleton and CAN be injected into
any service lifetime.
-
Supports
Named Options.
-
Supports reloadable changes. For new configuration values to be
reflected, the app DOES NOT need to be
restarted. It's recomputed on every request and cached for
the lifetime of the request.
-
Supports change notifications. That is if we want we can register
a
listener
to be called whenever a named TOptions changes.
- Usage:
builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("SomeSetting"));
...
app.MapGet("/config", (IOptionsMonitor<MyOptions> optionsDelegate) =>
{
return optionsDelegate.CurrentValue.Config;
});
Hope this helps.
Happy Coding.
Regards,
Jaliya