Introduction to IOptions Pattern in .Net.
Tags: softwareengineering, coding, codenewbie, dotnet
Published: 2023-06-04 16:09:00

**Options pattern** in .NET core is used to provide **strongly type classes** to access appsettings from the application or vault settings defined in the cloud. It provides encapsulation for that settings to be dependent on configured class and separated from other settings. These strongly typed classes can be created and can be validated across various services lifetime. we can configure **Options pattern** using three interfaces provided by .NET core.

1. IOptions<TOptions>
2. IOptionsSnapshot<TOptions>
3. IOptionsMonitor<TOptions>

we will learn about these 3 interfaces how they can be used, what's the differences between them and how to validate them. Lets start.

## IOptions<TOptions>

- We can only Read the configuration when app has started.
- Registered as singleton.
- Does not suppord **Named Options**.

## IOptionsSnapshot<TOptions>

- we can modify the configuration on each request.
- Registered as Scoped.
- Support **Named Options**

## IOptionsMonitor<TOptions> 
- we can modify the configuration on each request.
- Registered as singleton.
- Support **Named Options**
- OnChange Method is used to listen if any value has been changed in settings.(OnChange returns an object that implements IDisposable that you need to dispose)
- **Please Read About Performance issues on official Microsoft documentation** [link ](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-6.0). 

we will talk about **Named Options** and **Typed Options** later.

lets update our default appsettings file with company settings with name, address and zip code.

```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "Company": {
    "Name": "Company 1",
    "Address": "Robert Robertson, 1234 NW Bobcat Lane, St. Robert, MO 65584-5678",
    "ZipCode": 75780
  },
  "AllowedHosts": "*"
}

```
now create a C# class that matches our appsettings for companies the was defined before.

```C#
    public class CompanyOptions
    {
        public const string Options = "Company";

        public string Name { get; set; } = String.Empty;
        public string Address { get; set; } = string.Empty;
        public int ZipCode { get; set; } 
        public DateTime CreationDate { get; set; } = DateTime.UtcNow;

    }
```

Lets access our **Company Settings** in controller using **IOptions Interface**. we will use the all 3 Interface provided by .NET in our controller constructor and access them in our http request to see the results.

```C#
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace Options_Pattern.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly CompanyOptions _companyIOptions;
        private readonly CompanyOptions _companyIOptionsSnapshot;
        private readonly CompanyOptions _companyIOptionsMonitor;


        public WeatherForecastController(IOptions<CompanyOptions> companyIOptions,
            IOptionsSnapshot<CompanyOptions> companyIOptionsSnapshot,
            IOptionsMonitor<CompanyOptions> companyIOptionsMonitor)
        {
            _companyIOptions = companyIOptions.Value;
            _companyIOptionsSnapshot = companyIOptionsSnapshot.Value;
            _companyIOptionsMonitor = companyIOptionsMonitor.CurrentValue;
        }
        [HttpGet("[action]")]
        public IActionResult GetOptionsPattern()
        {
            var optionResponse = new 
            {
                IOptions = _companyIOptions.Name,
                IOptionsSnapShot = _companyIOptionsSnapshot.Name,
                IOptionsMonitor = _companyIOptionsMonitor.Name,
                CreationDate=_companyIOptions.CreationDate
            };
            return Ok(optionResponse);
        }

    }
}
```
You can see in the above code **IOptionsMonitor** is accessed using **CurrentValue** instead of **Value** because we can listen to **OnChange** if some value is changed in our settings.so we can perform other actions if required. Now lets run your project and see what's returned by **GetOptionsPattern** method in **postman**.


![Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i4ndq0w22hywlmjbd04q.png)

the default appsettings values has been returned. lets change the appsettings value while our project is running. we will change the name of company from **Company 1** to **Company 1 has been updated** save the appsettings file and run the **postman** request again. we can see the **IOptions** is not being updated but **iOptionsSnapShot** , **IOptionsMonitor** values has been updated.


![Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gp75nmv10la1qerk6pi.png)

we can also perform some action in **IOptionsMonitor OnChange** when any value has been updated. Now will update the company name write a into default .NET console the company name and update date then compare it with out postman request. let make some changes into out controller now the code looks like this now.

```C#
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace Options_Pattern.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private readonly CompanyOptions _companyIOptions;
        private readonly CompanyOptions _companyIOptionsSnapshot;
        private readonly CompanyOptions _companyIOptionsMonitor;


        private string CompanyName=string.Empty;
        public WeatherForecastController(IOptions<CompanyOptions> companyIOptions,
            IOptionsSnapshot<CompanyOptions> companyIOptionsSnapshot,
            IOptionsMonitor<CompanyOptions> companyIOptionsMonitor)
        {
            _companyIOptions = companyIOptions.Value;
            _companyIOptionsSnapshot = companyIOptionsSnapshot.Value;
            _companyIOptionsMonitor = companyIOptionsMonitor.CurrentValue;
            //listen to onchange and call our private OnComapnyValueChange method
            companyIOptionsMonitor.OnChange(updatedsettings => OnComapnyValueChange());
        }
        
        [HttpGet("[action]")]
        public IActionResult GetOptionsPattern()
        {
            var optionResponse = new 
            {
                IOptions = _companyIOptions.Name,
                IOptionsSnapShot = _companyIOptionsSnapshot.Name,
                IOptionsMonitor = _companyIOptionsMonitor.Name,
                CreationDate= _companyIOptionsMonitor.CreationDate
            };
            CompanyName = _companyIOptionsMonitor.Name;
            Console.WriteLine(CompanyName);
            return Ok(optionResponse);
        }
        private void OnComapnyValueChange()
        {

            Console.WriteLine($"{CompanyName} is called from OnValueChange Method{_companyIOptionsMonitor.CreationDate}");

        }

    }
}
```
reload the project again send a postman request to load over **options settings** which remains the same as before, also observe the console default value of **companyname** is being printed there without date as seen below.

![Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mst5cu0llqcamr1uenqp.png)


lets update **companyname** in appsettings and save it. now we can see without sending any request it prints to the console twice due to (the underlying file watcher triggering multiple times for the same change. The second call to Watch sometimes fires immediately because of this) further read [Link](https://github.com/dotnet/aspnetcore/issues/2542). The **OnCompanyValueChange** is fired due to **IOptionsMonitor.OnChange()**  without sending any request. its just monitoring the change and we are listening to it.

In the upcoming article we will talk about validation in **IOptions Patten** and Difference Between **Named and Typed Options** .