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

Milan Jovanović - Sep 16 '23 - - Dev Community

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; }
}
Enter fullscreen mode Exit fullscreen mode

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!"
}
Enter fullscreen mode Exit fullscreen mode

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"));
Enter fullscreen mode Exit fullscreen mode

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 IConfigureOptionsimplementation 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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>();
Enter fullscreen mode Exit fullscreen mode

When we try to inject our JwtOptions somewhere, the JwtOptionsSetup.Configuremethod 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;
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. 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.

  2. 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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .