Difference between IOptionsMonitor vs. IOptionsSnapshot
Asked Answered
B

4

64

According to this answer, IOptionsMonitor is registered in DI container as singleton and is capable of detecting changes through OnChange event subscription. It has a CurrentValue property.

On the other hand, IOptionsSnapshot is registered as scoped and also has a change detection capability by reading the last options for each request, but it doesn't have the OnChange event. It has a Value property.

Using both injected into a view for instance gives us the exact same behavior:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Options;
using UsingOptionsSample.Models;
using UsingOptionsSample.Services;

namespace UsingOptionsSample.Pages
{
    public class MyOptions
    {
        public MyOptions()
        {
            // Set default value.
            Option1 = "value1_from_ctor";
        }
        
        public string Option1 { get; set; }
        public int Option2 { get; set; } = 5;
    }

    public class OptionsTestModel : PageModel
    {
        private readonly MyOptions _snapshotOptions;
        private readonly MyOptions _monitorOptions;
        
        public OptionsTestModel(
            IOptionsMonitor<MyOptions> monitorOptionsAcessor, 
            IOptionsSnapshot<MyOptions> snapshotOptionsAccessor)
        {
            _snapshotOptions = snapshotOptionsAccessor.Value;
            _monitorOptions = monitorOptionsAcessor.CurrentValue;
        }

        public string SnapshotOptions { get; private set; }
        public string MonitorOptions { get; private set; }

        public void OnGetAsync()
        {
             //Snapshot options
            var snapshotOption1 = _snapshotOptions.Option1;
            var snapshotOption2 = _snapshotOptions.Option2;
            SnapshotOptions =
                $"snapshot option1 = {snapshotOption1}, " +
                $"snapshot option2 = {snapshotOption2}";

            //Monitor options
            var monitorOption1 = _monitorOptions.Option1;
            var monitorOption2 = _monitorOptions.Option2;
            MonitorOptions =
                $"monitor option1 = {monitorOption1}, " +
                $"monitor option2 = {monitorOption2}";
        }
    }
}

So, what's the point of having these two interfaces/implementations if they look like the same thing, just with different lifetimes? The code is based on this sample, which surprisingly doesn't include an IOptionsMonitor usage sample.

Why does one have a "Value" property and the other have a "CurrentValue" if both return the "current value" of an option?

Why/when should I use IOptionsSnapshot instead of IOptionsMonitor?

I don't think I got it straight, I must be missing some very important aspect regarding these and dependency injection.

Brahui answered 10/6, 2018 at 23:44 Comment(8)
IOptionsSnapshot has a scoped life time to guarantee you have the same configuration values during a single request. It could be odd behavior if the content changes in the mid request. Then one part of your request would apply the old values and the second part after the changes would apply new values, leading to conflicts and hard to track bugs. For everything that's related (or executed during) a request, you should use the snappshotDalhousie
@Tseng: But what does IOptionsMonitor<T> add that IOptionsSnapshot<T> doesn't? Why would one chose the monitor?Ladyfinger
Like I said, snapshot is used everywhere where you have a scoped context, since you don't want the value to be changed in mid of a request. See the docs about Reloading and Options factory, monitoring, and cache.Dalhousie
Essentially the monitors is for notifications whereas the snapshot is a cached version/snapshot of the IOptionsMonitor<T> and doesnt change during the requestDalhousie
i.e. IOptionsSnapshot<T> won't work if you inject it into a singleton serviceDalhousie
Differences with IOptions are described in andrewlock.net/…Vesicate
As @Dalhousie said it won't work with singleton services, IHostedService imp[lementations are one of which. they are used for long-background running services.Declare
I was also confused about this. Then I found HaoK's guidance and explained it clearly.Knap
P
29

The comments have some pretty good answers already. To try and summarize/repeat Tseng:

IOptionsSnapshot is great for getting injected into an object that is scoped or transient. It will be consistent for the lifetime of that object and new values will come in as you get new objects.

However, if you need options that reload in a singleton, IOptionsMonitor is what you should use, because your singleton will never change. A good example of such services are those inherited from IHostedService, for long-running background services in ASP.NET Core.

Phonon answered 28/9, 2018 at 22:39 Comment(0)
S
76

IOptionsMonitor is a singleton service that retrieves current option values at any time, which is especially useful in singleton dependencies.

IOptionsSnapshot is a scoped service and provides a snapshot of the options at the time the IOptionsSnapshot<T> object is constructed. Options snapshots are designed for use with transient and scoped dependencies.

Use IOptions<T> when you are not expecting your config values to change. Use IOptionsSnaphot<T> when you are expecting your values to change but want it to be consistent for the entirety of a request. Use IOptionsMonitor<T> when you need real time values.

Smithson answered 21/5, 2020 at 7:27 Comment(0)
P
29

The comments have some pretty good answers already. To try and summarize/repeat Tseng:

IOptionsSnapshot is great for getting injected into an object that is scoped or transient. It will be consistent for the lifetime of that object and new values will come in as you get new objects.

However, if you need options that reload in a singleton, IOptionsMonitor is what you should use, because your singleton will never change. A good example of such services are those inherited from IHostedService, for long-running background services in ASP.NET Core.

Phonon answered 28/9, 2018 at 22:39 Comment(0)
M
0

I think the main reason is whether you want to keep original configuration or get dynamic configuration changes.

IOptionsSnapshot allows you to access configuration from a specific point in time, a "snapshot". As configuration may change dynamically (without an app restart), IOptionsSnapshot is a good way to keep the original configuration from the time of registration.

IOptionsMonitor on the other hand, provides a way to track and get current configuration as well as updates to this configuration when there are dynamically reloaded changes at runtime. Another useful feature of IOptionsMontitor is the ability to get configuration a "named" key.

"Named" get example:

Program.cs 

builder.Services.Configure<TcpHealthProbeSettings>(
               "Liveness",
               config.GetSection($"{nameof(TcpHealthProbeSettings)}:{nameof(TcpLivenessProbe)}"));

builder.Services.Configure<TcpHealthProbeSettings>(
    "Readiness",
    config.GetSection($"{nameof(TcpHealthProbeSettings)}:{nameof(TcpReadinessProbe)}"));

Class constructor:

    protected TcpHealthProbeListener(
       // HealthCheckService healthCheckService,
        ILogger<TcpHealthProbeListener> logger,
        IOptionsMonitor<TcpHealthProbeSettings> options)
    {
        _settings = options.Get("Liveness");

        var port = _settings.TcpPort;
Machiavelli answered 29/5, 2024 at 17:15 Comment(0)
A
-1

In my project I need to read some values from configuration and merge them with auth token data. So, I use IOptionsSnapshot for Scoped storage, and have to update readonly Value property. No problem, update every property:

IOptionsMonitor<Company> companyConfig
  ...
  _companyConfig.Value.CompanyName = "...";
Ahmad answered 6/10, 2021 at 8:11 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.