In this week's newsletter I want to show you how you can use the powerful options pattern in ASP.NET Core 7.
The options pattern uses classes to provide strongly typed settings in your application at runtime.
The values for the options instance can come from multiple sources. The typical use case is to provide the settings from application configuration.
You can configure the options pattern in a few different ways in ASP.NET Core. I want to discuss some of the approaches and their potential benefits.
Creating The Options Class
I want to set the stage first, by creating the options class and explaining what settings we want to bind to it.
We want to configure JWT Authentication for our application, so we decided to create the JwtOptions
class to hold that configuration:
public class JwtOptions
{
public string Issuer { get; init; }
public string Audience { get; init; }
public string SecretKey { get; init; }
}
And let's imagine that inside of our appsettings.json
file we have the following configuration values:
"Jwt": {
"Issuer": "Gatherly",
"Audience": "Gatherly",
"SecretKey": "dont-tell-anyone!"
}
Alright, that's looking good. Now I want to show you a few ways to bind the values from JSON to our JwtOptions
class.
Setting Up Options Pattern Using IConfiguration
The most straightforward approach is to use the IConfiguration
instance that we can access while registering services.
We need to call the IServiceCollection.Configure<TOptions>
method, and specify the JwtOptions
as the generic argument:
builder.Services.Configure<JwtOptions>(
builder.Configuration.GetSection("Jwt"));
It doesn't get simpler than this, does it?
The only downside is that we are limited to the configuration values provided through application configuration.
This can be extended to include environment variables and user secrets also.
Setting Up Options Pattern Using IConfigureOptions
If you want a more robust approach, I have you covered. We're going to use the IConfigureOptions
interface to define a class to configure our strongly typed options.
There are two steps that we need to follow in this case:
- Create the
IConfigureOptions
implementation - Call
IServiceCollection.ConfigureOptions<TOptions>
with ourIConfigureOptions
implementation as the generic argument
To start off, we will create the JwtOptionsSetup
class:
public class JwtOptionsSetup : IConfigureOptions<JwtOptions>
{
private const string SectionName = "Jwt";
private readonly IConfiguration _configuration;
public JwtOptionsSetup(IConfiguration configuration)
{
_configuration = configuration;
}
public void Configure(JwtOptions options)
{
_configuration
.GetSection(SectionName)
.Bind(options);
}
}
We wrote more code, to achieve the same thing. Was it worth it?
Perhaps, if you consider that we now have access to dependency injection in the JwtOptionsSetup
class. This means that we can resolve other services that we can use to get the configuration values.
We also need to tell the application to use the JwtOptionsSetup
class:
builder.Services.ConfigureOptions<JwtOptionsSetup>();
When we try to inject our JwtOptions
somewhere, the JwtOptionsSetup.Configure
method will be called first the calculate the correct values.
Injecting Options With IOptions
We've seen a few examples for how to configure the options pattern with the JwtOptions
class.
But how do we actually use the options pattern?
Easy, you just need to inject IOptions<JwtOptions>
from the constructor.
I'll just show the JwtProvider
constructor here, for brevity.
public JwtProvider(IOptions<JwtOptions> options)
{
_options = options.Value;
}
The actual JwtOptions
instance is available on the IOptions<JwtOptions>.Value
property.
The IOptions
instance that we injected here is configured as a Singleton in dependency injection. This is very important to be aware of.
What About IOptionsSnapshot and IOptionsMonitor?
If you want to use the latest configuration values every time you inject an options class, then injecting IOptions
won't work.
However, you can use the IOptionsSnapshot
interface instead:
- It provides the latest configuration snapshot (cached per request)
- It is registered as a Scoped service
- It detects configuration changes after application start
You can also use the IOptionsMonitor
which retrieves the current option values at any time, and it's a Singleton service.
Wrapping up
The options pattern gives us a way to use strongly typed configuration classes in our application.
We can configure the options class in a simple way with IConfiguration
, or we can create an IConfigureOptions
implementation if we need something more powerful.
When it comes to using the options pattern , we have three approaches:
Deciding which of them to use in your application depends on what kind of behavior you want. If you don't need to support refreshing configuration values after application start, IOptions
is a perfect solution.
P.S. Whenever you're ready, there are 2 ways I can help you:
Pragmatic Clean Architecture: This comprehensive course will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture. Join 950+ students here.
Patreon Community: Think like a senior software engineer with access to the source code I use in my YouTube videos and exclusive discounts for my courses. Join 820+ engineers here.