Strongly Typed Configurations with Options Pattern
Tags: aspnetcore, namedoptions, optionspattern
Published: 2022-08-31 04:10:34

---
title: Strongly Typed Configurations with Options Pattern
published: true
date: 2022-08-29 01:44:22 UTC
tags: ASPNETCore,aspnetcore,namedoptions,optionspattern
canonical_url: https://coderethinked.com/strongly-typed-configurations-with-options-pattern/
---

In this post, we’ll see how to read the configuration file using the options pattern. The options pattern is recommended to read the app configuration in ASP.NET Core.

Before we see how to make configurations strongly typed the options pattern, we’ll see how to read the configuration settings using the `IConfiguration` interface.

## Reading Configuration settings with IConfiguration in ASP.NET Core

Let’s say we have the following configuration in our `appsettings.json` file

```json
{
  "Pattern": {
    "Name": "Options Pattern",
    "Version": 1
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}
```

And here’s how you read it

```c#
public class PrintOptionsController : ControllerBase
{
    private IConfiguration _configuration;

    public PrintOptionsController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpGet]
    public List<string> GetOptions()
    {
        var options = new List<string>();

        options.Add(_configuration["Pattern:Name"]);
        options.Add(_configuration["Pattern:Version"]);
        options.Add(_configuration["Logging:LogLevel:Default"]);

        return options;
    }
}
```

To read nested options, we’ve to use a colon (:) as a separator between the nested properties. Ex: `Pattern:Name`, `Pattern:Version`

If we run this, we should see this as the result

```
["Options Pattern","1","Information"]
```

This is working fine.

But, there’s a problem here. Every configuration value is a string no matter how you declare it in the `appsettings.json` file.

And we’ve to hard code or store the configuration keys somewhere in the constants file so that we don’t have to hardcode it every time we use it.

This is not great because we have to manually parse the configuration values to the appropriate type every time.

So, the preferred way to read configuration is to use strongly typed classes with Options pattern.

## What is the Options pattern?

> The options pattern uses classes to provide strongly typed access to groups of related settings.
> 
> <cite><br> From Microsoft </cite>

The Options pattern adheres to the following software principles:

1. Encapsulation or _**Interface Segregation Principle (ISP)**_: Classes that depend on configuration settings rely only on the configuration settings that they use.
2. _ **Separation of Concerns** _: As each class is tied to different settings in the configuration file they aren’t dependent or coupled.

## Rules for Options class to work

Our **options class should be**

- **Non-abstract** and should have a public parameterless constructor
- All public read-write properties of the type are bound

## Reading appsettings.json with Options pattern

The properties in our Options class should correspond to what we have in our `appsettings.json` file. We had the following JSON in our `appsettings.json` file which we wanted to be strongly typed.

```json
"Pattern": {
    "Name": "Options Pattern",
    "Version": 1
  }
```

So, our `PatternOptions` class should have two properties. Name and Version.

```c#
public class PatternOptions
{
    public const string SectionName = "Pattern";

    public string Name { get; set; }
    public int Version { get; set; }
}
```

We had the constant `SectionName`, which is not bound to the configurations. This is just to avoid hardcoding while configuring options pattern in the startup class.

Now, let’s configure our `PatternOptions` in the startup class.

In our ConfigureServices method in the startup class, add the following line

```c#
services.Configure<PatternOptions>(Configuration.GetSection(PatternOptions.SectionName));
```

That’s it! Our `PatternOptions` class is now good to go. We can inject our class anywhere and read the configurations.

## Injecting our PatternOptions class with IOptions interface

Instead of reading the configuration from `IConfiguration` interface we will now read from `IOptions<T>`. The T is our PatternOptions.

So, let’s remove the `IConfiguration` injection from our `PrintOptionsController` and inject `IOptions` with `PatternOptions` as a type.

```c#
public class PrintOptionsController : ControllerBase
{
    private PatternOptions _patternOptions;

    public PrintOptionsController(IOptions<PatternOptions> options)
    {
        _patternOptions = options?.Value;
    }

    [HttpGet("withOptionsPattern")]
    public PatternOptions GetOptionsWithIOptions()
    {
        return _patternOptions;
    }
}
```

Within the constructor, we have assigned `options?.Value` to `_patternOptions` variable. Or we could only get the `.Value` only in the action method we need.

If we run the app, we should get our options from the configuration file.

```
{"name":"Options Pattern","version":5}
```

## What are Named options?

Named options are used when we have multiple sections that bind to the same properties.

Let’s say we have the following in our `appsettings.json` file

```json
"Seasons": {
    "Winter": {
      "Month": "December", 
      "Temperature": 25
    },
    "Summer": {
      "Month": "April", 
      "Temperature": 45
    },
    "Monsoon": {
      "Month": "July",
      "Temperature": 35
    } 
  }
```

Now, if we’ve to make options pattern out of this, we’d have to create 3 different classes (1 for winter, 1 for summer, and the other for monsoon) and bind these in the section configuration in the startup class.

But, with Named options, we can have the following class and use it for all seasons.

```c#
public class SeasonsOptions
{
    public const string Winter = "Winter";
    public const string Summer = "Summer";
    public const string Monsoon = "Monsoon";

    public string Month { get; set; }
    public int Temperature { get; set; }
}
```

Notice we have the common properties for all 3 seasons (Month and Temperature) and we have the constant variables for season names (these are not bound to the configuration as said earlier we’ll use them for configuration).

And in the startup, we can register the sections individually using the same SeasonsOptions class.

```c#
services.Configure<SeasonsOptions>(SeasonsOptions.Winter, Configuration.GetSection("Seasons:Winter"));
services.Configure<SeasonsOptions>(SeasonsOptions.Summer, Configuration.GetSection("Seasons:Summer"));
services.Configure<SeasonsOptions>(SeasonsOptions.Monsoon, Configuration.GetSection("Seasons:Monsoon"));
```

Once we are done with the setup we can modify our print options controller to be something like this

```c#
public class PrintOptionsController : ControllerBase
{
    private SeasonsOptions _winterSeasonOptions;
    private SeasonsOptions _summerSeasonOptions;
    private SeasonsOptions _monsoonSeasonOptions;

    public PrintOptionsController(IOptionsSnapshot<SeasonsOptions> namedWinterOptions)
    {

        _winterSeasonOptions = namedWinterOptions.Get(SeasonsOptions.Winter);
        _summerSeasonOptions = namedWinterOptions.Get(SeasonsOptions.Summer);
        _monsoonSeasonOptions = namedWinterOptions.Get(SeasonsOptions.Monsoon);
    }

    [HttpGet("getSeasonOptions")]
    public List<SeasonsOptions> GetAllSeasons()
    {
        return new List<SeasonsOptions>
        {
            _winterSeasonOptions,
            _summerSeasonOptions,
            _monsoonSeasonOptions
        };
    }
}
```

Notice we are using `IOptionsSnapshot` instead of `IOptions` here. This is because the **`IOptions` interface does not support Named options**.

`IOptionsSnapshot` interface is generally used when options should be recomputed on every request. Yes, there’s a performance penalty with this as it is a scoped service and needs to be recomputed per request. So, use it with caution.

Now, if we run the app we should see all the seasons and their temperatures.

```json
[{"month":"December","temperature":25},{"month":"April","temperature":45},{"month":"July","temperature":35}]
```

That’s it! Thanks for reading.

Please comment below and let me know your thoughts on the options pattern in ASP.NET Core.

## References

- [Options Pattern in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-6.0)
- [Options Interfaces](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-6.0#options-interfaces)
- [Named options in ASP.NET core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-6.0#named-options-support-using-iconfigurenamedoptions)

The post [Strongly Typed Configurations with Options Pattern](https://coderethinked.com/strongly-typed-configurations-with-options-pattern/) appeared first on [Code Rethinked](https://coderethinked.com).