How To Use The Options Pattern In ASP.NET Core 7

How To Use The Options Pattern In ASP.NET Core 7

4 min read ·

Thank you to our sponsors who keep this newsletter free to the reader:

QuadSpinner Highlighter is an open-source Visual Studio extension that lets you highlight important objects and arbitrary texts to help you navigate your code more easily.

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.

Let's dive in.

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 our IConfigureOptions 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.


Whenever you're ready, there are 4 ways I can help you:

  1. (COMING SOON) REST APIs in ASP.NET Core: You will learn how to build production-ready REST APIs using the latest ASP.NET Core features and best practices. It includes a fully functional UI application that we'll integrate with the REST API. Join the waitlist!
  2. Pragmatic Clean Architecture: Join 3,600+ students in this comprehensive course that 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.
  3. Modular Monolith Architecture: Join 1,600+ engineers in this in-depth course that will transform the way you build modern systems. You will learn the best practices for applying the Modular Monolith architecture in a real-world scenario.
  4. Patreon Community: Join a community of 1,000+ engineers and software architects. You will also unlock access to the source code I use in my YouTube videos, early access to future videos, and exclusive discounts for my courses.
  5. Promote yourself to 61,000+ subscribers by sponsoring this newsletter.

Become a Better .NET Software Engineer

Join 61,000+ engineers who are improving their skills every Saturday morning.