基于选项模式实现.NET Core的配置热更新( 二 )


  • IOptionsMonitor:生命周期为 Singleton , 可以随时检索当前配置项 。 应用启动后 , 对配置的修改是响应式的 。
  • 是不是听起来有一点还有一点绕?长话短说就是 , 如果希望修改完配置立即生效 , 那么 , 更推荐使用 IOptionsSnapshot 和IOptionsMonitor , 前者是在下一次请求时生效 , 后者则是访问CurrentValue的时候生效 。 而对于像3.14或者0.618这种运行时期间不会修改的“常量” , 更推荐使用IOptions 。 下面是关于它们的一个例子:
    [ApiController][Route("[controller]")]public class WeatherForecastController : ControllerBase{ private readonly ILogger _logger; private readonly IOptions _learningOptions; private readonly IOptionsSnapshot _learningOptionsSnapshot; private readonly IOptionsMonitor _learningOptionsMonitor; private readonly IConfiguration _configuration; public WeatherForecastController(ILogger logger, IOptions learningOptions, IOptionsSnapshot learningOptionsSnapshot, IOptionsMonitor learningOptionsMonitor, IConfiguration configuration) { _logger = logger; _learningOptions = learningOptions; _learningOptionsSnapshot = learningOptionsSnapshot; _learningOptionsMonitor = learningOptionsMonitor; _configuration = configuration; _learningOptionsMonitor.OnChange((options, value) => { _logger.LogInformation($"OnChnage => {JsonConvert.SerializeObject(options)}"); }); } [HttpGet("{action}")] public ActionResult GetOptions { var builder = new StringBuilder; builder.AppendLine("learningOptions:"); builder.AppendLine(JsonConvert.SerializeObject(_learningOptions.Value)); builder.AppendLine("learningOptionsSnapshot:"); builder.AppendLine(JsonConvert.SerializeObject(_learningOptionsSnapshot.Value)); builder.AppendLine("learningOptionsMonitor:"); builder.AppendLine(JsonConvert.SerializeObject(_learningOptionsMonitor.CurrentValue)); return Content(builder.ToString); }}【基于选项模式实现.NET Core的配置热更新】现在我们修改一下配置文件 , 因为我们为_learningOptionsMonitor注册了回调函数 , 可以在控制台看到对应的日志:
    基于选项模式实现.NET Core的配置热更新文章插图
    此时 , 我们通过 Postman 调用接口 , 我们会得到下面的结果:
    基于选项模式实现.NET Core的配置热更新文章插图
    可以注意到 , 此时 , learningOptions 中的值依然是更新前的值 , 这就是它们三者的区别 , 清楚了吗?
    除了这些以外 , 选项模式(Options)中还有一个需要注意的地方 , 是所谓的命名选项(IConfigureNamedOptions) , 主要用在多个 Section 绑定统一属性时 。 譬如现在的应用程序都流行深色主题 , 实际上深色主题和浅色主题具有相同的结构 , 比如前景色和背景色 , 两者唯一的区别是这些颜色配置不一样 。 考虑下面的配置信息:
    { "Themes": { "Dark": { "Foreground": "#fff", "Background": "#000" }, "White": { "Foreground": "#000", "Background": "#fff" } }}此时 , 我们该如何定义这个主题选项呢?
    public class ThemeOptions{ public string Foreground { get; set; } public string Background { get; set; }}接下来 , 我们通过命名的方式来注入两个不同的主题:
    services.Configure("DarkTheme", Configuration.GetSection("Themes:Dark"));services.Configure("WhiteTheme", Configuration.GetSection("Themes:White"));在任何你希望使用它们的地方 , 注入 IOptionsSnapshot 和IOptionsMonitor 即可 , 这两个类型都提供了一个Get 方法 , 传入前面定义好的主题就可以获取到对应的主题了 。 细心的朋友 , 应该会发现一件事情 , 这里三剑客只提到了后面两个 , IOptions 直接被无视了 。 请记住下面这段话:命名的选项只能通过 IOptionsSnapshot 和 IOptionsMonitor 来访问 。 所有选项都是命名实例 。 IConfigureOptions 实例将被视为面向 Options.DefaultName 实例 , 即 string.Empty 。 IConfigureNamedOptions 还可实现 IConfigureOptions 。 IOptionsFactory 的默认实现具有适当地使用每个实例的逻辑 。命名选项用于面向所有命名实例 , 而不是某一特定命名实例 。 ConfigureAll 和 PostConfigureAll 使用此约定 。