Dov
Dov

Reputation: 16186

Why is IOptionsMonitor<T>.OnChange not being called?

I'd like to have my .Net Core 3.1 app automatically reload its configuration as the file changes on disk, but I'm having trouble getting this to work with the Options pattern. I run my app, save changes to the config file, and it's never called. Why doesn't the IOptionsMonitor instance ever call the OnChange handler? What am I missing?

Program.cs IHostBuilder creation

Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration(config => configuration = config.Build())
    .ConfigureServices((hostContext, services) => {
        var separateConfig = new ConfigurationBuilder()
            .AddJsonFile("SeparateConfig.json", optional: false, reloadOnChange: true)
            .Build();

        services
            .AddSingleton<MyMainService>()
            .Configure<MySeparateConfig>(c => separateConfig.Bind(c));
    })

Service that uses MySeparateConfig

public class MyMainService
{
    public MyMainService(IOptionsMonitor<MySeparateConfig> config)
    {
        if (config is null) throw new ArgumentNullException(nameof(config));

        ConfigChangeHandle = config.OnChange(UpdateConfiguration);
        
        // Perform initial configuration using config.CurrentValue here
    }

    private IDisposable ConfigChangeHandle { get; set; }
    
    private void UpdateConfiguration(MySeparateConfig config)
    {
        // Never called
    }
}

Upvotes: 6

Views: 11218

Answers (2)

0xced
0xced

Reputation: 26558

For the reload mechanism to work with IOptionsMonitor<TOptions>, an IOptionsChangeTokenSource<TOptions> needs to be registered in the services.

If you need an OptionsBuilder<TOptions>, for example to add validation, this can be achieved with AddOptions + BindConfiguration (from the Microsoft.Extensions.Options.ConfigurationExtensions NuGet package if you don't have a dependency in ASP.NET Core)

services.AddOptions<MyConfig>().BindConfiguration("").Validate(…)

Note that the BindConfiguration extensions method automatically registers the IOptionsChangeTokenSource<TOptions> which is the magic required for the reload mechanism to work.

Upvotes: 11

Dov
Dov

Reputation: 16186

As @Nkosi pointed out in comments, this line was the problem:

    // Wrong
    .Configure<MySeparateConfig>(c => separateConfig.Bind(c));

When I replaced it with the line below, everything started working right:

    // Right
    .Configure<MySeparateConfig>(separateConfig);

Upvotes: 15

Related Questions