In this tutorial, we are going to cover an approach through which we can read the configuration data in .NET Core - the options pattern.

Why the Options Pattern?

Options pattern gives the below features

Please refer to the Microsoft documentation for more details.

Tools which I have used for this tutorial:

  1. Visual Studio 2022 Community Edition – Preview version 17.4 (Preview 2)
  2. .NET 6.0
  3. Web API
  4. Swagger

To begin with, let us create a sample project. The source code can be downloaded from GitHub.

Let us assume that we have the below configuration in appsettings.json

"OptionsTutorial": {
    "InterfaceName" : "IOptions"
}

Create the following class:

public class OptionsTutorial
{
    public const string Position = "OptionsTutorial";
    public string InterfaceName { get; set; }
}

Rules which need to be followed to create this class:

Bind the configuration data to strongly typed objects.

In Program.cs class, we need to add the below line to bind the configuration data.

builder.Services
    .Configure< OptionsTutorial>(builder.Configuration.GetSection(OptionsTutorial.Position));

Use IOptions<T> Interface

This interface is registered as a Singleton and can be injected into any service lifetime.

This does not support:

Let us make a few code changes in WeatherForecastController.cs

Add the below variable at the beginning of the class.

private OptionsTutorial _optionsTutorial;

make the below code changes in Constructor.

public WeatherForecastController(ILogger<WeatherForecastController> logger, IOptions<OptionsTutorial> options)
{
    _logger = logger;
    this._optionsTutorial = options.Value;
}

Introduce a new action method as below

[HttpGet]
[Route("optionspattern")]
public IActionResult OptionsPattern()
{
    var interfaceName = this._optionsTutorial.InterfaceName;
    var message = $"interface name from OptionsTutorial - {interfaceName}";
    return Ok(message);
}

Let us debug the action method and the result.

Options Pattern in .NET 6.0

We can see that; the configuration data has been bound to the strongly typed object.

Use IOptionsSnapshot<T> Interface

Add the below settings in Appsettings.json

"SnapShot": {
    "InterfaceName": "IOptionsSnapshot"
}

Add the below interface and classes

public class SnapShot
{
    public const string Position = "Snapshot";
    public string InterfaceName { get; set; }
}

I have created two interfaces - ISnapShotCheckWithScope.cs and ISnapShotCheckWithSingleton.cs for validating the Singleton as well as Scoped service lifetime

Make the necessary changes in Program.cs

builder.Services.AddScoped<ISnapShotCheckWithScope, SnapShotCheckWithScope>();
/// UnComment this line and see whether the code executed or not.
/// you will get a runtime error
//builder.Services.AddSingleton<ISnapShotCheckWithSingleton, SnapShotCheckWithSingleton>();

As IOptionsSnapshot interface has been registered as Scoped, it cannot be injected into Singleton Services. Please look at the source code available in GitHub to understand more on this.

Let us go ahead and execute the Scoped controller.

Options Pattern in .NET 6.0

This interface will read updated configuration data after the app has started.

Use IOptionsMonitor<T> Interface

Let us go through the code snippets.

Appsettings.json changes

"MonitorOption": {
    "InterfaceName": "IOptionsMonitor"
}

A few classes added as below

Options Class

public class MonitorOption
{
    public const string Position = "MonitorOption";
    public string InterfaceName { get; set; }  
}

Interface and classes as below

public interface IOptionsMonitorCheck
{
    Task<string> CheckOptionMonitor();
}

public class OptionMonitorCheck : IOptionsMonitorCheck
{
    private readonly MonitorOption _monitorOption;
    public OptionMonitorCheck(IOptionsMonitor<MonitorOption> optionsMonitor) =>
        (_monitorOption) = (optionsMonitor.CurrentValue);
    public Task<string> CheckOptionMonitor()
    {
        var interfaceName = this._monitorOption.InterfaceName;
        return Task.FromResult(interfaceName);
    }
}

Controller as below

public class OptionMonitorController : ControllerBase
{
    private readonly IOptionsMonitorCheck _optionsMonitorCheck;
    public OptionMonitorController(IOptionsMonitorCheck monitorCheck)=>
        (_optionsMonitorCheck) = (monitorCheck);
    [HttpGet]
    [Route("optionsMonitor")]
    public IActionResult OptionsMonitor()
    {
        var interfaceName = this._optionsMonitorCheck.CheckOptionMonitor();
        return Ok(interfaceName);
    }
}

Changes in Program.cs as below

builder.Services.AddScoped<IOptionsMonitorCheck, OptionMonitorCheck>();
//Uncomment each of the below one at a time and see how this behaves
//builder.Services.AddSingleton<IOptionsMonitorCheck, OptionMonitorCheck>();
//builder.Services.AddTransient<IOptionsMonitorCheck, OptionMonitorCheck>();

Let us go ahead and debug the controller OptionMonitorController.cs. Here we are registering the interface – IoptionMonitorCheck as Scope.

Options Pattern in .NET 6.0

Now change from Scoped to Singleton in Program.cs and see how this behaves.

//builder.Services.AddScoped<IOptionsMonitorCheck, OptionMonitorCheck>();
//Uncomment each of the below one at a time and see how this behaves
builder.Services.AddSingleton<IOptionsMonitorCheck, OptionMonitorCheck>();
//builder.Services.AddTransient<IOptionsMonitorCheck, OptionMonitorCheck>();

Options Pattern in .NET 6.0

Successfully execute.

Now let us change to Transient in Program.cs and see.

//builder.Services.AddScoped<IOptionsMonitorCheck, OptionMonitorCheck>();
//Uncomment each of the below one at a time and see how this behaves
//builder.Services.AddSingleton<IOptionsMonitorCheck, OptionMonitorCheck>();
builder.Services.AddTransient<IOptionsMonitorCheck, OptionMonitorCheck>();

Options Pattern in .NET 6.0

Execute successfully.

We have seen how to read configuration data using Options pattern in .NET 6.0. This approach will ensure two engineering principles – encapsulation and separation of concerns. Hope this article helps you to understand the Options pattern at high level. Thank you for reading my article and please leave your comments in the comment box below.