Store and retrieve settings using IOptions pattern in C#
Table of contents
Introduction - Importance of managing settings in applications
Storing and reading settings/configurations is a common scenario in any application. General examples include connection string, app settings, etc.
Reading such settings should be easy, strongly typed, easy to maintain, and less room for error. In this blog post, I will show how to achieve this using IOptions<>
pattern.
Ways to store settings in .NET
Commonly you can store settings in the following places in .NET
appsettings.json
user secrets (secrets.json)
environment variables
I have created a minimal API project using Visual Studio with default settings for this blog post.
Retrieve settings using IConfiguration
appsettings.json
Create a new section in appsettings.json
with the following data.
"settings": {
"key1" : "value1"
}
Inject IConfiguration
delegate to the API method and retrieve the above value by calling configuration.GetValue("settings:key1")
.
Environment variables
You can also retrieve the value from the environment file in the same way above. The project has launchSettings.json
file where you can add environment settings under profiles --> https (any profile you used to run). Get the value by calling configuration.GetValue("key2").
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"settings:key2": "value2"
}
User secrets
You can store sensitive data in user secrets during development, this will be stored in %APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json
You can create the above file using either running dotnet user-secrets init
in the project path or right-click on the project and click "manage user secrets".
For this current example, please add the below section to the secrets.json
file.
"settings": {
"key3": "value3"
}
When to use user secrets? If you want to store database credentials, API keys, or other sensitive information that should not be part of the git repository, you can use user secrets. This is particularly useful for public repositories.
You can retrieve the user secrets in the same way as above configuration.GetValue("settings:key3")
app.MapGet("/settings", (IConfiguration configuration) => new
{
fromAppSetting = configuration.GetValue<string>("settings:key1"),
FromEnv = configuration.GetValue<string>("settings:key2"),
FromUserSecrets = configuration.GetValue<string>("settings:key3"),
})
.WithName("GetSettings")
.WithOpenApi();
Retrieve settings using IOptions
pattern
What is the IOptions
Pattern?
The IOptions
pattern in .NET Core is a design approach for handling configuration settings in a type-safe way. It allows binding configuration sections to strongly typed classes and injecting these classes into application components. This pattern offers benefits like type safety, validation, and the ability to reload settings when they change.
Implement IOptions
pattern in asp.net core C
Consider the above appsettings.json
example. Create a class for the settings as below.
public class Settings
{
public string Key1 { get; set; }
}
Next register IOptions
services with the above Settings
class.
builder.Services.AddOptions<Settings>().BindConfiguration(nameof(Settings));
The above code registers a new Settings
instance as a singleton and binds the settings
section data from configuration (app settings, user secrets, or environment variables).
You can inject IOptions<Settings>
to anywhere in the application to access the settings. Check the updated code below.
app.MapGet("/settings", (IConfiguration configuration, IOptions<Settings> options) => new
{
fromAppSetting = configuration.GetValue<string>("settings:key1"),
FromEnv = configuration.GetValue<string>("key2"),
FromUserSecrets = configuration.GetValue<string>("key3"),
FromOptions = options.Value.Key1,
FromOptions2 = options.Value.Key2,
FromOptions3 = options.Value.Key3
})
.WithName("GetSettings")
.WithOpenApi();
By calling the above method, returns the setting value from appsettings.json
but in a strongly typed way.
Please note that each key/value pair comes from different configurations but IOptions
pattern still recognizes the same and combines all the values into a single section.
Settings validations in IOptions
pattern - ValidateDataAnnotations
You can validate settings using data annotations in the settings class as below. Here we have added Required
condition the Key1
which is validated against the empty value.
public class Settings
{
[Required(AllowEmptyStrings = false)]
public string Key1 { get; set; }
public string Key2 { get; set; }
public string Key3 { get; set; }
}
You should also add ValidateDataAnnotations
to the options pattern registration to make the validations possible.
builder.Services.AddOptions<Settings>()
.BindConfiguration(nameof(Settings))
.ValidateDataAnnotations();
Now update the Key1
as empty value, build the project, and navigate to the above endpoint. This will throw the validation error
You can see here, the validation is only happening when you are trying to access the settings and not on application startup.
ValidateOnStart
You can add ValidateOnStart()
to the above options pattern, this enforces validation check on application startup instead of runtime. This will be useful to avoid errors at runtime and we can avoid any surprises at a later time.
If validation fails, the application won't start and throws the actual validation error as above.
Using IOptionsSnapshot and IOptionsMonitor
Since IOptions
pattern is reading data from configuration only on application startup, later changes won't be available unless you restart the application.
So, for reading the latest available settings from the configuration, we have two interfaces IOptionsSnapshot
and IOptionsMonitor
.
IOptionsSnapshot
- It's registered as scoped, and the values are fetched from the configuration whenever a request starts. If the settings are changed during the request process, it won't be available for the same request but available for the next request. Options snapshots are designed for use with transient and scoped dependencies.
IOptionsMonitor
- It's registered as a singleton and always fetches the latest value from the configuration. It is beneficial in singleton dependencies.
References
Options Pattern In .NET 6.0 (c-sharpcorner.com)
How to use the IOptions pattern for configuration in ASP.NET Core RC2 (andrewlock.net)
Using the IOptions Pattern in a .NET Core Application β andrewhalil.com
Introduction to IOptions Pattern in .Net. - DEV Community