In the previous article , we discussed how to read data from appsettings.json by IOptions . In this article, we will examine the differences between IOptions, IOptionsMonitor and IOptionsSnapshot.

The IOptions interface works as a Singleton and therefore can be injected into all services with any lifetime (Lifetime). If you change the value of the appsettings.json file after running the program and use this interface to read the data, you will not see your changes, because this service is Singleton and is only validated at the time of running the program. If the program does not run again, it shows the same initial values.

The IOptionsSnapshot interface works as Scoped. That is, for each request, it re-reads the data from appsettings.json and provides it to us. This interface cannot be used in Singleton lifetime services.

The IOptionsMonitor interface works as a Singleton, but the difference with the IOptions interface is that if a change is made to the appsettings.json file, new changes can be received by the OnChange method. For example, we created a class called ApplicationConfig that has a property called Name, and the value of this property is called appsettings.json.

public class ApplicationConfig
{
    public string Name { get; set; }
}

 Appsettings.json file :

{
  "ApplicationConfig": {
    "Name": "DotNetDocs"
  }
}

 Then we introduce this class to the Configure method in the ConfigureService method: 

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<ApplicationConfig>(Configuration.GetSection(nameof(ApplicationConfig)));
   //...
}

Next we create a controller to test the received values:

[Route("api/[controller]")]
[ApiController]
public class HomeController : Controller
{
    private readonly ApplicationConfig _options;
    private readonly ApplicationConfig _optionsSnapshot;
    public HomeController(IOptions<ApplicationConfig> options, IOptionsSnapshot<ApplicationConfig> optionsSnapshot)
    {
        _options = options.Value;
        _optionsSnapshot = optionsSnapshot.Value;
    }

    [HttpGet("Index")]
    public IActionResult Index()
    {
        var options = _options.Name;
        var snapshot = _optionsSnapshot.Name;

        return Json(new { options = options, snapshot = snapshot });
    }
}

Now if you enter /api/Home/Index in the browser, you will get the following value:

{"options":"DotNetDocs","snapshot":"DotNetDocs"}

If you change the Name value in the appsetting.json file to dntips and reload the page, you will get the following values:

{"options":"DotNetDocs","snapshot":"dntips"}

The Name value received from the IOptionsSnapshot interface receives the changes, but the IOptions interface does not receive the changes because it is registered as a Singleton.

To test how IOptionsMonitor works, we create a hosting service called HostedService, in the manufacturer of which we change the OnChange method according to our needs:

public class HostedService : BackgroundService
{
    private ApplicationConfig _optionsMonitor;

    public HostedService(IOptionsMonitor<ApplicationConfig> optionsMonitor)
    {
        _optionsMonitor = optionsMonitor.CurrentValue;
        optionsMonitor.OnChange(config =>
        {
            _optionsMonitor = config;
        });
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var name = _optionsMonitor.Name;
            await Task.Delay(1000);
        }
    }
}

Register hosting service:

public void ConfigureServices(IServiceCollection services)
{
    //...
    services.AddHostedService<HostedService>();
}

Now if you run the program, the host service will also run and read the values from appsettings.json once and put the Name value inside the name variable next time (in ExecuteAsync method). But when a change is made in the appsettings.json file, the OnChange method is called and puts the new values in the config parameter of the OnChange method, and using this method we can get the new changes and change the _optionsMonitor.

  • If you have a service that is registered as Singleton and reads a set of values from appsettings.json, if the data may change, it is best to use IOptionsMonitor to apply the changes using the OnChange method. The IOptionsMonitor interface is more commonly used in Singleton services such as HostedServices because IOptionsSnapshot cannot be used to refresh data.
  • If the changes to the appsettings.json file are not very important and should not be applied immediately, you can use IOptions.
  • If the changes to the appsettings.json file are important and need to be changed, you can use IOptionsSnapshot because it re-reads the data from appsettings.json for each request. This interface cannot be used in Singleton services.

Resources :

;)