OPTIONS PATTERN IN .NET 6
Tags: csharp, dotnet, dotnetcore, backend
Published: 2023-10-03 20:34:20

Hello everyone, in a previous article, we saw how to retrieve the configurations of our applications through the IConfiguration interface. Now, we will move on to using a method that, in my opinion, is certainly cleaner and more responsive: the Options Pattern.

### Table of Contents:
* [**The Options Pattern**](#the-options-pattern)
* [**Configuration with IOptionsSnapshot**](#configuration-with-ioptionssnapshot)
* [**Configuration with IOptionsMonitor**](#configuration-with-ioptionsmonitor)
* [**Configuring Named Options**](#configuring-named-options)
* [**Choosing Between Configurations**](#choosing-between-configurations)

### **The Options Pattern**

The Options Pattern provides us with strongly typed access to our configurations, thanks to the use of classes, and centralized configuration through the use of configuration in the Program.cs file. Configuration retrieval is achieved through the use of the three main interfaces that this paradigm offers, namely:

* **IOptions**
* **IOptionsSnapshot**
* **IOptionsMonitor**

Let's try to create a simple basic configuration using the Options Pattern. First, let's add a fictitious configuration to our appsettings.json file for educational purposes:

```json

"OptionsConfigurationBase": {
  "Test1": "It's a test configuration"
}

```

Next, we will create our class with properties that match the ones in the previously configured appsettings section:

```csharp

public class OptionsConfigurationBase
{
    public const string ConfigurationName = nameof(OptionsConfigurationBase);
    public string Test1 { get; set; }
}

```
Once done, let's configure our class in the **Program.cs** file using the extension method **.Configure** 

```csharp

builder.Services.Configure<OptionsConfigurationBase>(
    builder.Configuration
        .GetSection(OptionsConfigurationBase.ConfigurationName)
);

```

so that it can be subsequently injected through the Dependency Injection of **.NET Core**.

```csharp

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    //.... other variable

    private readonly ILogger<WeatherForecastController> _logger;
    private readonly OptionsConfigurationBase _options;

    public WeatherForecastController(ILogger<WeatherForecastController> logger, IOptions<OptionsConfigurationBase> options)
    {
        _logger = logger;
        _options = options.Value ?? throw new ArgumentNullException(nameof(options));
    }

    // ...other methods

    [HttpGet]
    [Route("GetOptionsTest")]
    public string GetOptionsTest()
    {
        return _options.Test1;
    }
}


```
Now, this is certainly a great system; the only issue is that this configuration does not allow for reading changes at runtime. Therefore, any modifications made after the application starts will not be considered. To achieve this type of behavior, we'll need to use the **IOptionsSnapshot** interface.

### **Configuration with IOptionsSnapshot**

This configuration is very useful when options need to be recalculated for each request. Essentially, this is what the **IOptionsSnapshot<T>** interface does: it is registered as a Scoped service and caches the configurations for each request.

There are no changes in registration compared to the IOptions interface, except for the fact that in the constructor of our controller, we change **IOptions<T>** to **IOptionsSnapshot<T>**.

```csharp

public WeatherForecastController(ILogger<WeatherForecastController> logger, IOptionsSnapshot<OptionsConfigurationBase> options)
{
        _logger = logger;
        _options = options.Value ?? throw new ArgumentNullException(nameof(options));
}

```
If, after the application is started, we change the value of the "Test1" parameter from:

```json
"Test1": "It's a test configuration"
```

to:

```json
"Test1": "It's a test configuration updated"
```

And if we make the request again after changing the "Test1" parameter to "It's a test configuration" we will see that the returned result will be **"It's a test configuration updated"**.

This configuration is certainly useful, but it should be used with caution because it could potentially lead to performance issues since, as a Scoped service, it is recalculated for every request.

### **Configuration with IOptionsMonitor**

Last but not least, the use of the IOptionsMonitor<T> interface is certainly important. This service is configured as a Singleton service and will retrieve updated values of our configurations at any time. One key difference is found in the constructor when retrieving the injected service, instead of using the .Value property, we will use the .CurrentValue property:

```csharp

public WeatherForecastController(ILogger<WeatherForecastController> logger, IOptionsMonitor<OptionsConfigurationBase> options)
{
        _logger = logger;
        _options = options.CurrentValue ?? throw new 
ArgumentNullException(nameof(options));
}

```

Exactly as before, when making the initial call, we will receive the response with the value **"It's a test configuration updated"** However, if we update the property to **"I'm a test configuration updated and retrieved with IOptionsMonitor"** this value will be retrieved from our service.

### **Configuring Named Options**

Named options are useful when we need multiple sections within a single configuration object.

Let's modify the **appsettings.json** file as follows:

```json

"OptionsConfigurationBase": {
  "NamedOptions1": {
    "Test1": "I am the Named Option 1 configuration"
  },
  "NamedOptions2": {
    "Test1": "I am the Named Option 2 configuration"
  }
}

```

let's make a small modification to our class:

```csharp

public class OptionsConfigurationBase
{
    public const string NamedOptions1 = "NamedOptions1";
    public const string NamedOptions2 = "NamedOptions2";

    public string Test1 { get; set; }
}

```

finally, let's modify the **Program.cs** to support this new configuration option:

```csharp

builder.Services.Configure<OptionsConfigurationBase>(
    OptionsConfigurationBase.NamedOptions1,
    builder.Configuration.GetSection($"{nameof(OptionsConfigurationBase)}:{OptionsConfigurationBase.NamedOptions1}")
);

builder.Services.Configure<OptionsConfigurationBase>(
    OptionsConfigurationBase.NamedOptions2,
    builder.Configuration.GetSection($"{nameof(OptionsConfigurationBase)}:{OptionsConfigurationBase.NamedOptions2}")
);

```

How do we retrieve configurations in the controller? By using the **Get** method associated with the **IOptionsSnapshot<T>** interface, like this:

```csharp

private readonly  OptionsConfigurationBase _namedOptions1;
private readonly  OptionsConfigurationBase _namedOptions1;

public WeatherForecastController(ILogger<WeatherForecastController> logger, IOptionsSnapshot<OptionsConfigurationBase> options)
{
        _logger = logger;
        _namedOptions1 = options.Value.Get(OptionsConfigurationBase.NamedOptions1)?? throw new ArgumentNullException(nameof(options));
        _namedOptions1 = options.Value.Get(OptionsConfigurationBase.NamedOptions2)?? throw new ArgumentNullException(nameof(options));
}

```

### **Choosing Between Configurations**

As we've seen, each of these interfaces has its own characteristics, and choosing between them is not always straightforward. However, we can do a quick review to help you choose the right interface based on your needs:

#### **IOptions<T>**
* It doesn't support runtime reloading of configuration values.
* It's registered as a Singleton service in the .NET Core Dependency Injection container.
* It can be injected into any service, whether registered as Singleton, Scoped, or Transient.
* It doesn't support named options.

#### **IOptionsSnapshot<T>**

* Useful when configurations need to be reconfigured for each request.
* Registered as Scoped, which means it cannot be injected into a Singleton service and might have performance issues.
* Supports named options.

#### **IOptionsMonitor<T>**

* Registered as a Singleton service in the .NET Core Dependency Injection container.
* Supports named options.
* Dynamic changes reloaded for each request.

And with that, our overview of the Options Pattern in .Net Core comes to an end. I hope this article helps you choose the right interface and understand how this pattern makes configuration retrieval in our applications easy and dynamic.

You can find all the code used in the article (it's not much) in this repository:

{% github Maurizio8788/OptionsPatternArticle %}

If you enjoyed the article, please share it and give it a like to make it visible to others. I look forward to reading your comments where you share your experiences with this feature.

Happy Coding!