IOptionsMonitor not tracking changes

23 hours ago 3
ARTICLE AD BOX

Your scenario with AddInMemoryCollection is not supported per docs:

The IOptionsMonitor type supports change notifications and enables scenarios where your app may need to respond to configuration source changes dynamically. This is useful when you need to react to changes in configuration data after the app has started. Change notifications are only supported for file-system based configuration providers, such as the following:

Microsoft.Extensions.Configuration.Ini Microsoft.Extensions.Configuration.Json Microsoft.Extensions.Configuration.KeyPerFile Microsoft.Extensions.Configuration.UserSecrets Microsoft.Extensions.Configuration.Xml

However, I think the docs only mean the providers that come out of the box.

Creating custom IConfigurationSource/IConfigurationProvider should enable you to have reloadable in memory providers too.

A minimal implementation from asp.net core's own tests

private class ReloadableMemorySource : IConfigurationSource { public IConfigurationProvider Build(IConfigurationBuilder builder) { return new ReloadableMemoryProvider(); } } private class ReloadableMemoryProvider : ConfigurationProvider { public override void Set(string key, string value) { base.Set(key, value); OnReload(); } }

and the test which is not that different from your own test code.

[Theory] [MemberData(nameof(CreateNonEmptyBuilderFuncs))] // empty builder doesn't enable HostFiltering public async Task WebApplicationConfiguration_HostFilterOptionsAreReloadable(CreateBuilderFunc createBuilder) { var builder = createBuilder(); var host = builder.WebHost .ConfigureAppConfiguration(configBuilder => { configBuilder.Add(new ReloadableMemorySource()); }); await using var app = builder.Build(); var config = app.Services.GetRequiredService<IConfiguration>(); var monitor = app.Services.GetRequiredService<IOptionsMonitor<HostFilteringOptions>>(); var options = monitor.CurrentValue; Assert.Contains("*", options.AllowedHosts); var changed = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); monitor.OnChange(newOptions => { changed.TrySetResult(); }); config["AllowedHosts"] = "NewHost"; await changed.Task.TimeoutAfter(TimeSpan.FromSeconds(10)); options = monitor.CurrentValue; Assert.Contains("NewHost", options.AllowedHosts); }
Read Entire Article