上一篇 ASP.NET Core - 选项系统之选项配置 中提到 IOptions
1. IOptions
- IOptions
对象的生命周期是 Singleton (单例),它可以在任意地方进行注入使用 - 该接口对象在第一次使用的时候被实例化,并且选项类中的内容会一直保持不变,前面也提过选项类内容可以在配置来源修改之后更新,但是通过 IOption
解析的选项类不会随着更新而改变 - IOptions
接口不支持命名选项模式,它是没有 get 方法的,也并不会默认读取第一个,它只能读取 String.Empty 默认命名的选项,如果没有配置默认选项的话,虽然也能解析出 Options 选项类对象,但是对象的属性都是相应类型的默认值(引用类型是 null,值类型是 0,其他的也都是相应类型的默认值)
public class OptionController : ControllerBase
{
private readonly BlogOptions _blogOptions;
public OptionController(IOptions<BlogOptions> options)
{
// 通过 IOptions<TOptions> 接口的 Value 属性读取选项类
// 选项类始终是程序启动时加载的值,不会改变
_blogOptions = options.Value;
}
}
2. IOptionsMonitor
- IOptionsMonitor
对象的生命周期是 Scoped(作用域),Scoped 生命周期的特点是不能注入到 Singleton 服务中 - 在作用域中(最常见的一次Http请求),创建 IOptionsSnapshot
对象实例时,会从配置中读取最新选项值作为快照,并在当前作用域中始终使用该快照。也就是说一次请求中选项类内容保持不变,但是不同请求中可能因为配置来源的修改而不同 - IOptionsMonitor
支持命名选项
public class OptionController : ControllerBase
{
private readonly BlogOptions _blogOptions;
public OptionController(IOptionsSnapshot<BlogOptions> optionsSnapshot)
{
// IOptionsSnapshot<TOptions> 可以通过 Value 属性读取默认的命名的选项类, Options 对象实例创建时读取的配置快照
_blogOptions = optionsSnapshot.Value;
// 也可以通过 Get 方法获取某一个命名选项,没有指定命名时,默认命名为 string.Empty
//_blogOptions = optionsSnapshot.Get(string.Empty);
}
}
3. IOptionsSnapshot
- IOptionsSnapshot
对象的生命周期也是 Singleton (单例) - 通过 IOptionsSnapshot
接口注入的对象每次读取选项值时,都是从配置中读取最新选项值,能够实时获取配置来源的更改 - 该接口支持命名选项模式
- 除了可以查看 TOptions 的值,还可以监控 TOptions 配置的更改,支持重新加载配置(CurrentValue),并当配置发生更改时,进行通知(OnChange),支持缓存与缓存失效 (IOptionsMonitorCache
),每次调用实例的 CurrentValue 时,会先检查缓存(IOptionsMonitorCache )是否有值,如果有值,则直接用,如果没有,则从配置中读取最新选项值,并记入缓存。当配置发生更改时,会将缓存清空。
public class OptionController : ControllerBase
{
private readonly BlogOptions _blogOptions;
public OptionController(IOptionsMonitor<BlogOptions> optionsMonitor)
{
// IOptionsMonitor<TOptions> 接口没有 Value 属性,通过 CurrentValue 获取选项类对象,
// 每次调用 CurrentValue都会实时读取配置源,始终是最新配置的值
_blogOptions = optionsMonitor.CurrentValue;
// 该接口也支持通过 Get 方法获取命名选项
_blogOptions = optionsMonitor.Get(string.Empty);
// 可以通过 OnChange 注册事件,当配置被加载时会触发事件
optionsMonitor.OnChange(OnOptionsChange);
}
[HttpGet]
public Task<BlogOptions> Get()
{
return Task.FromResult(_blogOptions);
}
private void OnOptionsChange(BlogOptions options)
{
Console.WriteLine(JsonSerializer.Serialize(options));
}
}
启动应用,调用一次 Get 接口,在 Api 控制器构造函数中注册了配置加载触发事件,之后修改 appsettings.json 配置文件中选项类对于的配置节点内容,可以看到事件触发,控制台中输出了改变之后的选项类内容。
4. 三个接口的选项读取机制演示
三个接口解析的选项类的差别,可以通过以下测试清楚得看出:
配置文件中初始选项节点如下:
"Blog": {
"Title": "ASP.NET Core Options11",
"Content": "This is a blog about Options System in ASP.NET Core Framework.",
"CreateTime": "2022-12-06"
}
这里为了方便看出 Scoped 生命周期 IOptionSnapeshoot
public class OptionController : ControllerBase
{
private readonly IOptions<BlogOptions> _blogOptions;
private readonly IOptionsSnapshot<BlogOptions> _blogSnapshotOptions;
private readonly IOptionsMonitor<BlogOptions> _blogMonitorOptions;
public OptionController(
IOptions<BlogOptions> options,
IOptionsSnapshot<BlogOptions> optionsSnapshot,
IOptionsMonitor<BlogOptions> optionsMonitor
)
{
// 注意这里不能再把选项类对象先读取出来,否则选项类对象也不会再改变了
_blogOptions = options;
_blogSnapshotOptions = optionsSnapshot;
_blogMonitorOptions = optionsMonitor;
}
[HttpGet]
public Task Get()
{
Console.WriteLine("第一次读取配置:");
Console.WriteLine("IOptions<TOptions>:" + JsonSerializer.Serialize(_blogOptions.Value));
Console.WriteLine("IOptionsSnapshot<TOptions>:" + JsonSerializer.Serialize(_blogSnapshotOptions.Value));
Console.WriteLine("IOptionsMonitor<TOptions>:" + JsonSerializer.Serialize(_blogMonitorOptions.CurrentValue));
Console.WriteLine("请修改配置文件!");
Console.ReadKey();
Console.WriteLine("第二次读取配置:");
Console.WriteLine("IOptions<TOptions>:" + JsonSerializer.Serialize(_blogOptions.Value));
Console.WriteLine("IOptionsSnapshot<TOptions>:" + JsonSerializer.Serialize(_blogSnapshotOptions.Value));
Console.WriteLine("IOptionsMonitor<TOptions>:" + JsonSerializer.Serialize(_blogMonitorOptions.CurrentValue));
return Task.CompletedTask;
}
}
之后启动应用调用 Get 接口,并在过程中将配置文件内容修改为以下:
"Blog": {
"Title": "ASP.NET Core Options222",
"Content": "This is a blog about Options System in ASP.NET Core Framework.",
"CreateTime": "2022-12-06"
}
可以看到控制台的输出中,第二次读取配置的时候,IOptionsMonitor
之后不要关闭应用,再调一次 Get 接口,并在过程中再次修改配置如下:
"Blog": {
"Title": "ASP.NET Core Options333",
"Content": "This is a blog about Options System in ASP.NET Core Framework.",
"CreateTime": "2022-12-06"
}
这一次的 Get 请求的输出结果如下:
可以看到 IOptionsMonitor
参考文章:
ASP.NET Core 中的选项模式 | Microsoft Learn
选项模式 - .NET | Microsoft Learn
面向 .NET 库创建者的选项模式指南 - .NET | Microsoft Learn
理解ASP.NET Core - 选项(Options)
ASP.NET Core 系列:
目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core - 选项系统之选项配置