DEV Community

Fabrizio Bagalà
Fabrizio Bagalà

Posted on • Edited on

Options Pattern in ASP.NET

Options pattern is a powerful and versatile configuration method in .NET, particularly in ASP.NET. It allows developers to group related settings into coherent classes, facilitating the management and injection of configurations throughout the application code.

Implementation

Creating and implementing the Options pattern follows a series of key steps. First, we need to add in the settings file appsettings.json or appsettings.{Environment}.json the values we need:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "MyOptions": {
    "Options1": "hello world",
    "Options2": 12
  }
}
Enter fullscreen mode Exit fullscreen mode

Subsequently, it's necessary to define a class for the settings:

public class MyOptions
{
    public string? Option1 { get; set; }
    public int Option2 { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

These settings must be configured in the Program.cs file:

builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("MyOptions"));
Enter fullscreen mode Exit fullscreen mode

Finally, the settings can be injected into the necessary parts of the application via dependency injection.

public class MyService
{
    private readonly MyOptions _myOptions;

    public MyService(IOptions<MyOptions> myOptionsAccessor)
    {
        _myOptions = myOptionsAccessor.Value;
    }
}
Enter fullscreen mode Exit fullscreen mode

Read the configuration settings

To access configuration settings, you can use one of these two methods:

  1. GetSection(string key): Returns an IConfigurationSection that represents a specific part of the configuration settings from the provided key. If the section isn't found in the configuration settings, this method will not throw an exception but rather will return a section with no value.
  2. GetRequiredSection(string key): Similar to GetSection, it also returns an IConfigurationSection representing a specific part of the configuration settings from the provided key. However, if the section isn't found in the configuration settings, GetRequiredSection will throw an exception (InvalidOperationException).

IOptions, IOptionsSnapshot, and IOptionsMonitor

Once the Options pattern is implemented, there are three main interfaces that can be used to access the options: IOptions<T>, IOptionsSnapshot<T>, and IOptionsMonitor<T>. Each of these interfaces has a specific use case and provides unique advantages.

  • IOptions: This is the simplest way to access the options. The IOptions<T> instance is created at the application's start and remains the same for the duration of the application. It does not detect changes to the configuration settings during the application's runtime.
  • IOptionsSnapshot: This interface is particularly useful in ASP.NET applications that handle multiple simultaneous requests. IOptionsSnapshot<T> creates a new instance of the options for each request, meaning it can detect changes to configuration settings between different requests.
  • IOptionsMonitor: IOptionsMonitor<T> is similar to IOptionsSnapshot<T> in that it can detect changes to configuration settings. However, unlike IOptionsSnapshot<T>, it does not create a new instance of the options for each request. Instead, it uses a notification mechanism to update the options when it detects a change. This makes it more efficient than IOptionsSnapshot<T> when settings change rarely.

The comparison between these three interfaces fundamentally comes down to the frequency of changes to configuration settings and the need to detect these changes. If your settings never change during the application's runtime, IOptions<T> is probably the best choice. If your settings change frequently and you need to detect these changes for every request, then IOptionsSnapshot<T> might be the best choice. If your settings change but not very frequently, IOptionsMonitor<T> offers a good balance between updating settings and efficiency.

Benefits

Options pattern offers some advantages:

  • It brings together related settings: Settings that are logically related can be grouped together in a single class. This makes the code more readable and easier to manage.
  • Provides strong typing of settings: Settings are strongly typed, which means you will get IntelliSense support and type checks at compile time.
  • Facilitates testing: Settings can be easily faked in tests, which makes it easier to write unit tests.

Named Options and Post-configuration

To handle more complex configurations, the Options pattern also provides support for named options and post-configuration.

Named options allow for configuring different instances of the same options class. This is particularly useful if you need to use different sets of configurations within the same application.

builder.Services.Configure<MyOptions>("option1", builder.Configuration.GetSection("Option1"));
builder.Services.Configure<MyOptions>("option2", builder.Configuration.GetSection("Option2"));
Enter fullscreen mode Exit fullscreen mode

Named options can then be retrieved using IOptionsSnapshot<T> or IOptionsMonitor<T>.

public class MyService
{
    private readonly MyOptions _option1;
    private readonly MyOptions _option2;

    public MyService(IOptionsSnapshot<MyOptions> options)
    {
        _option1 = options.Get("option1");
        _option2 = options.Get("option2");
    }
}
Enter fullscreen mode Exit fullscreen mode

Post-configuration provides the ability to configure the options after they have been initialized within the Program.cs file. This is useful if you need to set some options based on others.

builder.Services.PostConfigure<MyOptions>(options =>
{
    if (string.IsNullOrEmpty(options.Option1))
    {
        options.Option1 = "Default Value";
    }
});
Enter fullscreen mode Exit fullscreen mode

In this example, if Option1 has not been configured, it is set to "Default Value".

Options validation

Options pattern supports options validation. You can create a validation class by implementing the IValidateOptions interface. For instance:

using Microsoft.Extensions.Options;

public class MyOptionsValidation : IValidateOptions<MyOptions>
{
    public ValidateOptionsResult Validate(string name, MyOptions options)
    {
        return string.IsNullOrEmpty(options.Option1) 
            ? ValidateOptionsResult.Fail("Option1 is required.") 
            : ValidateOptionsResult.Success;
    }
}
Enter fullscreen mode Exit fullscreen mode

Then, you can register this validation class in the Program.cs file:

using Microsoft.Extensions.Options;

builder.Services.AddSingleton<IValidateOptions<MyOptions>, MyOptionsValidation>();
Enter fullscreen mode Exit fullscreen mode

If the settings do not pass validation, the application throws an exception at startup.

Conclusion

Options pattern in ASP.NET provides a powerful configuration method. It allows grouping settings into strongly typed classes, offers the ability to monitor changes to settings, supports settings validation, and more. Depending on your specific needs, IOptions<T>, IOptionsSnapshot<T> and IOptionsMonitor<T> offer various ways to handle and access your configuration settings.

References

Top comments (2)

Collapse
 
rmaurodev profile image
Ricardo

Nice!

Collapse
 
fabriziobagala profile image
Fabrizio Bagalà

Thank you 🙏