The Options Pattern in .NET: typed configuration in 3 lines of code
Still using appsettings.json with Configuration["api_url"] scattered across your C# classes? And discovering config errors only when production breaks?
With the Options Pattern, your configuration becomes a typed C# object, validated at application startup. I use it in every project.
The problem: magic strings
Without the Options Pattern, your code looks like this:
var apiUrl = _configuration["ApiSettings:BaseUrl"];
var timeout = _configuration.GetValue<int>("ApiSettings:TimeoutInSeconds");
The result: magic strings everywhere, silent typos, and if the config structure changes, you have to touch every class that uses it.
The solution, in 4 steps
1, The section in appsettings.json
{
"ApiConfig": {
"Token": "a1b2c3d4e5f6g7h8i9j0",
"ApiUrl": "https://api.antoine.fr",
"TimeoutInSeconds": 30
}
}
2, The C# class that represents it
public class ApiConfig
{
public string Token { get; init; }
public string ApiUrl { get; init; }
public int TimeoutInSeconds { get; init; }
}
3, The registration in Program.cs: the famous 3 lines
builder.Services
.AddOptions<ApiConfig>()
.BindConfiguration("ApiConfig")
.ValidateDataAnnotations();
4, Typed injection into your services
public class MyService
{
private readonly HttpClient _httpClient;
private readonly ApiConfig _apiConfig;
public MyService(HttpClient httpClient, IOptions<ApiConfig> options)
{
_httpClient = httpClient;
_apiConfig = options.Value;
}
public async Task<string> GetDataFromApiAsync()
{
var response = await _httpClient.GetAsync($"{_apiConfig.ApiUrl}/endpoint");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
What it changes day to day
Organized: one dedicated class per section of your appsettings.json.
Typed: you access options.ApiUrl instead of Configuration["api_url"], a property instead of a magic string.
Safe: impossible to misspell the config, your IDE warns you if the property doesn't exist.
Validated: add [Required] on your properties to check their presence at startup, no more bad surprises in production.
Simple: 3 lines in Program.cs and it's set up for the whole app.
Adding a new config?
Need a Datadog object in your appsettings.json? Create a DatadogSettings class mirroring the section, register it in Program.cs, and it's ready to use, typed and validated.
Takeaway: the Options Pattern takes minutes to set up and durably improves the readability, maintainability and robustness of your .NET projects.
This article comes from one of my LinkedIn posts (in French), join the discussion ↗