In this post, we’ll see how to read the configuration file using the options pattern. The options pattern is recommended to read the app configuration in ASP.NET Core.
Before we see how to make configurations strongly typed the options pattern, we’ll see how to read the configuration settings using the IConfiguration
interface.
Let’s say we have the following configuration in our appsettings.json
file
{ "Pattern": { "Name": "Options Pattern", "Version": 1 }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } } }
And here’s how you read it
public class PrintOptionsController : ControllerBase { private IConfiguration _configuration; public PrintOptionsController(IConfiguration configuration) { _configuration = configuration; } [HttpGet] public List<string> GetOptions() { var options = new List<string>(); options.Add(_configuration["Pattern:Name"]); options.Add(_configuration["Pattern:Version"]); options.Add(_configuration["Logging:LogLevel:Default"]); return options; } }
To read nested options, we’ve to use a colon (:) as a separator between the nested properties. Ex: Pattern:Name
, Pattern:Version
If we run this, we should see this as the result
["Options Pattern","1","Information"]
This is working fine.
But, there’s a problem here. Every configuration value is a string no matter how you declare it in the appsettings.json
file.
And we’ve to hard code or store the configuration keys somewhere in the constants file so that we don’t have to hardcode it every time we use it.
This is not great because we have to manually parse the configuration values to the appropriate type every time.
So, the preferred way to read configuration is to use strongly typed classes with Options pattern.
The options pattern uses classes to provide strongly typed access to groups of related settings.
From Microsoft
The Options pattern adheres to the following software principles:
Our options class should be
The properties in our Options class should correspond to what we have in our appsettings.json
file. We had the following JSON in our appsettings.json
file which we wanted to be strongly typed.
"Pattern": { "Name": "Options Pattern", "Version": 1 }
So, our PatternOptions
class should have two properties. Name and Version.
public class PatternOptions { public const string SectionName = "Pattern"; public string Name { get; set; } public int Version { get; set; } }
We had the constant SectionName
, which is not bound to the configurations. This is just to avoid hardcoding while configuring options pattern in the startup class.
Now, let’s configure our PatternOptions
in the startup class.
In our ConfigureServices method in the startup class, add the following line
services.Configure<PatternOptions>(Configuration.GetSection(PatternOptions.SectionName));
That’s it! Our PatternOptions
class is now good to go. We can inject our class anywhere and read the configurations.
Instead of reading the configuration from IConfiguration
interface we will now read from IOptions<T>
. The T is our PatternOptions.
So, let’s remove the IConfiguration
injection from our PrintOptionsController
and inject IOptions
with PatternOptions
as a type.
public class PrintOptionsController : ControllerBase { private PatternOptions _patternOptions; public PrintOptionsController(IOptions<PatternOptions> options) { _patternOptions = options?.Value; } [HttpGet("withOptionsPattern")] public PatternOptions GetOptionsWithIOptions() { return _patternOptions; } }
Within the constructor, we have assigned options?.Value
to _patternOptions
variable. Or we could only get the .Value
only in the action method we need.
If we run the app, we should get our options from the configuration file.
{"name":"Options Pattern","version":5}
Named options are used when we have multiple sections that bind to the same properties.
Let’s say we have the following in our appsettings.json
file
"Seasons": { "Winter": { "Month": "December", "Temperature": 25 }, "Summer": { "Month": "April", "Temperature": 45 }, "Monsoon": { "Month": "July", "Temperature": 35 } }
Now, if we’ve to make options pattern out of this, we’d have to create 3 different classes (1 for winter, 1 for summer, and the other for monsoon) and bind these in the section configuration in the startup class.
But, with Named options, we can have the following class and use it for all seasons.
public class SeasonsOptions { public const string Winter = "Winter"; public const string Summer = "Summer"; public const string Monsoon = "Monsoon"; public string Month { get; set; } public int Temperature { get; set; } }
Notice we have the common properties for all 3 seasons (Month and Temperature) and we have the constant variables for season names (these are not bound to the configuration as said earlier we’ll use them for configuration).
And in the startup, we can register the sections individually using the same SeasonsOptions class.
services.Configure<SeasonsOptions>(SeasonsOptions.Winter, Configuration.GetSection("Seasons:Winter")); services.Configure<SeasonsOptions>(SeasonsOptions.Summer, Configuration.GetSection("Seasons:Summer")); services.Configure<SeasonsOptions>(SeasonsOptions.Monsoon, Configuration.GetSection("Seasons:Monsoon"));
Once we are done with the setup we can modify our print options controller to be something like this
public class PrintOptionsController : ControllerBase { private SeasonsOptions _winterSeasonOptions; private SeasonsOptions _summerSeasonOptions; private SeasonsOptions _monsoonSeasonOptions; public PrintOptionsController(IOptionsSnapshot<SeasonsOptions> namedWinterOptions) { _winterSeasonOptions = namedWinterOptions.Get(SeasonsOptions.Winter); _summerSeasonOptions = namedWinterOptions.Get(SeasonsOptions.Summer); _monsoonSeasonOptions = namedWinterOptions.Get(SeasonsOptions.Monsoon); } [HttpGet("getSeasonOptions")] public List<SeasonsOptions> GetAllSeasons() { return new List<SeasonsOptions> { _winterSeasonOptions, _summerSeasonOptions, _monsoonSeasonOptions }; } }
Notice we are using IOptionsSnapshot
instead of IOptions
here. This is because the IOptions
interface does not support Named options.
IOptionsSnapshot
interface is generally used when options should be recomputed on every request. Yes, there’s a performance penalty with this as it is a scoped service and needs to be recomputed per request. So, use it with caution.
Now, if we run the app we should see all the seasons and their temperatures.
[{"month":"December","temperature":25},{"month":"April","temperature":45},{"month":"July","temperature":35}]
That’s it! Thanks for reading.
Please comment below and let me know your thoughts on the options pattern in ASP.NET Core.
Karthik is a passionate Full Stack developer working primarily on .NET Core, microservices, distributed systems, VUE and JavaScript. He also loves NBA basketball so you might find some NBA examples in his posts and he owns this blog.
In this post, we’ll see how to test gRPC Server applications using different clients. And… Read More
In this post, we'll create a new gRPC project in ASP.NET Core and see what's… Read More
In this blog post, we’ll see how to run dotnet core projects without opening visual… Read More
Programmatically evaluating policies is useful when we want to provide access or hide some data… Read More
We saw how we could set up policy-based authorization in our previous article. In this… Read More
What is policy-based authorization and how to set up policy-based authorization with handlers and policies… Read More
This website uses cookies.