IOptionsMonitor未追踪InMemory配置变更问题求助
问题:InMemory配置修改后未触发Options变更通知
代码实现
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; namespace ConfigGames { internal class MyOptions { public string? MyId { get; set; } } internal class Program { static async Task Main(string[] args) { Console.WriteLine("Hello, World!"); HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); builder.Configuration.AddInMemoryCollection(new Dictionary<string, string?>() { ["Value:MyId"] = Guid.NewGuid().ToString() }); builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("Value")); builder.Services.AddHostedService<Runner>(); IHost host = builder.Build(); var runTask = host.RunAsync(); await Task.Delay(1000); // 获取当前值 var opMonitor = host.Services.GetRequiredService<IOptionsMonitor<MyOptions>>(); Console.WriteLine(opMonitor.CurrentValue.MyId); // 追踪变更 opMonitor.OnChange(newValue => { Console.WriteLine($"New value: {newValue.MyId}"); }); // 修改配置值 host.Services.GetRequiredService<IConfiguration>().GetSection("Value")["MyId"] = Guid.NewGuid().ToString(); await Task.Delay(1000); // 期望获取新值 Console.WriteLine(opMonitor.CurrentValue.MyId); await Task.Delay(1000); await host.StopAsync(); await runTask; } } }
运行输出
Hello, World! info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down. info: Microsoft.Hosting.Lifetime[0] Hosting environment: Production info: Microsoft.Hosting.Lifetime[0] Content root path: C:\Projects\ConfigGames\ConfigGames\bin\Debug\net10.0 d1369bd5-31ee-49a4-8443-1edc50b1f38f d1369bd5-31ee-49a4-8443-1edc50b1f38f info: Microsoft.Hosting.Lifetime[0] Application is shutting down... C:\Projects\ConfigGames\ConfigGames\bin\Debug\net10.0\ConfigGames.exe (process 15488) exited with code 0 (0x0). Press any key to close this window . . .
预期结果
- 输出两个不同的MyId值
- 触发OnChange回调,输出“New value: {newValue.MyId}”消息
原因分析
- MemoryConfigurationProvider不主动触发变更通知:
AddInMemoryCollection添加的默认内存配置源,其底层的MemoryConfigurationProvider没有实现配置变更的发布机制。直接通过IConfiguration索引器修改值,只是更新了内存中的数据,但不会通知配置系统发生了变化。 - IOptionsMonitor依赖配置变更事件:
IOptionsMonitor的缓存更新和OnChange回调触发,完全依赖配置源发布的变更事件。由于内存配置源没有触发该事件,所以IOptionsMonitor无法感知到值的变化,始终返回初始缓存的配置值。
解决方法
要让InMemory配置支持变更通知,需要手动管理配置源并触发重载事件:
修改后的代码
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; namespace ConfigGames { internal class MyOptions { public string? MyId { get; set; } } internal class Program { static async Task Main(string[] args) { Console.WriteLine("Hello, World!"); HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); // 创建可手动更新的内存配置源 var memorySource = new MemoryConfigurationSource { InitialData = new Dictionary<string, string?>() { ["Value:MyId"] = Guid.NewGuid().ToString() } }; var memoryProvider = new MemoryConfigurationProvider(memorySource); builder.Configuration.Add(memoryProvider); builder.Services.Configure<MyOptions>(builder.Configuration.GetSection("Value")); builder.Services.AddHostedService<Runner>(); IHost host = builder.Build(); var runTask = host.RunAsync(); await Task.Delay(1000); // 获取当前值 var opMonitor = host.Services.GetRequiredService<IOptionsMonitor<MyOptions>>(); Console.WriteLine(opMonitor.CurrentValue.MyId); // 追踪变更 opMonitor.OnChange(newValue => { Console.WriteLine($"New value: {newValue.MyId}"); }); // 修改配置值并触发变更通知 var newMyId = Guid.NewGuid().ToString(); memoryProvider.Set("Value:MyId", newMyId); // 触发配置系统重载,通知所有监听者 ((IConfigurationRoot)host.Services.GetRequiredService<IConfiguration>()).Reload(); await Task.Delay(1000); // 获取更新后的新值 Console.WriteLine(opMonitor.CurrentValue.MyId); await Task.Delay(1000); await host.StopAsync(); await runTask; } } }
关键修改点
- 手动创建
MemoryConfigurationProvider并保存引用,替代AddInMemoryCollection,这样可以直接操作配置源修改值; - 使用
memoryProvider.Set()方法更新配置值,确保配置源内部数据同步; - 调用
IConfigurationRoot.Reload()触发配置变更事件,让IOptionsMonitor感知到变化,更新缓存并执行OnChange回调。
内容的提问来源于stack exchange,提问作者Volker
相关产品推荐
相关产品推荐

