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);
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);
// ################## let's get our value
var opMonitor = host.Services.GetRequiredService<IOptionsMonitor<MyOptions>>();
Console.WriteLine(opMonitor.CurrentValue.MyId);
// ################## and track changes
opMonitor.OnChange(newValue =>
{
Console.WriteLine($"New value: {newValue.MyId}");
});
// ################## trying to change the value
host.Services.GetRequiredService<IConfiguration>().GetSection("Value")["MyId"] = Guid.NewGuid().ToString();
await Task.Delay(1000);
// ################## now the new value should be there
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 . . .
我期望的是:
- 两个不同的值
New value信息。
有什么想法吗?
解决方案
并非所有配置提供者都需要实现IOptionsMonitor所需的重新加载功能,才能让OnChange和CurrentValue按你期望的方式工作。不幸的是,随AddInMemoryCollection一起提供的MemoryConfigurationProvider并不是内置的、支持该功能的提供者之一。请参阅 文档:
变更通知 仅对基于文件系统的配置提供者提供支持,例如如下所示:
Microsoft.Extensions.Configuration.Ini Microsoft.Extensions.Configuration.Json Microsoft.Extensions.Configuration.KeyPerFile Microsoft.Extensions.Configuration.UserSecrets Microsoft.Extensions.Configuration.Xml
创建自定义 IConfigurationSource/IConfigurationProvider 是唯一的解决方案。
基于来自 asp.net core自己的测试 的最小实现,以及 MemoryConfigurationProvider 的源代码,我们可以创建一个类似于现有的 AddInMemoryCollection 的便捷扩展方法 AddReloadableInMemoryCollection:
public static class ReloadableMemoryConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddReloadableInMemoryCollection(this IConfigurationBuilder configurationBuilder) {
ArgumentNullException.ThrowIfNull(configurationBuilder);
configurationBuilder.Add(new ReloadableMemoryConfigurationSource());
return configurationBuilder;
}
public static IConfigurationBuilder AddReloadableInMemoryCollection(
this IConfigurationBuilder configurationBuilder,
IEnumerable<KeyValuePair<string, string?>>? initialData) {
ArgumentNullException.ThrowIfNull(configurationBuilder);
configurationBuilder.Add(new ReloadableMemoryConfigurationSource { InitialData = initialData });
return configurationBuilder;
}
private class ReloadableMemoryConfigurationSource : IConfigurationSource
{
public IEnumerable<KeyValuePair<string, string?>>? InitialData { get; set; }
public IConfigurationProvider Build(IConfigurationBuilder builder) {
return new ReloadableMemoryConfigurationProvider(this);
}
}
private class ReloadableMemoryConfigurationProvider : ConfigurationProvider
{
private readonly ReloadableMemoryConfigurationSource _source;
public ReloadableMemoryConfigurationProvider(ReloadableMemoryConfigurationSource source) {
ArgumentNullException.ThrowIfNull(source);
_source = source;
if (_source.InitialData != null) {
foreach (KeyValuePair<string, string?> pair in _source.InitialData) {
Data.Add(pair.Key, pair.Value);
}
}
}
public override void Set(string key, string value) {
base.Set(key, value);
OnReload();
}
}
}
如果你把代码改为使用它,你就会得到你期望的结果。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。