ASP.NET Core 网站运行时修改设置如何自动生效

在ASP.NET Core中,如果修改了appsettings.json中的设置,那么默认情况下就得重启网站才能生效。有没有办法在修改设置后自动刷新并应用呢?

背景

首先,我们看看默认模板建出来的 ASP.NET Core 网站,配置文件有两个:

appsettings.json

appsettings.Development.json

前者用于生产环境,后者用于开发环境,在Debug模式下,会优先使用 appsettings.Development.json 的设置。

在不显示指定的情况下,Program.cs 中的CreateWebHostBuilder() 方法会读取这两个设置文件。为了便于维护,大家通常会创建一个对应的class,用来强类型匹配设置项。比如这样:

services.Configure<AppSettings>(Configuration.GetSection(nameof(AppSettings)));

使用的时候用IOptions接口注入:

public Ctor(IOptions<AppSettings> settings)

如果你还不了解这种方法,可以参见我之前写的文章:https://edi.wang/post/2016/10/9/read-appsettings-aspnet-core (微信可能屏蔽了我的域名,复制到浏览器能打开)

问题

这种方法确实可以读取配置文件,并使用强类型约束和使用。但个缺点就是在网站运行时,如果修改了 appsettings.json 中的配置项,是不会当场生效的,必须重启网站才能应用。

比如我博客的网页标题,是来源于配置文件里的 SiteTitle,如果我在网站运行时登录服务器后台,改成 "Edi.Wang Test",是不会生效的,必须得等下次网站重启。

解决办法

还是刚才我博客的例子,我给标题赋值用的方法是在Razor页面里注入IOptions接口:

@inject IOptions<AppSettings> Settings

然后赋值:

@Settings.Value.SiteTitle

解决办法非常简单,换个接口,用IOptionsSnapshot就行啦,在C#类的构造函数里注入的话也是一样的改法:

@inject IOptionsSnapshot<AppSettings> Settings

对比一下这两个接口:

IOptions
// Summary:
//     Used to retrieve configured TOptions instances.
//
// Type parameters:
//   TOptions:
//     The type of options being requested.
public interface IOptions<out TOptions> where TOptions : class, new()
IOptionsSnapshot
// Summary:
//     Used to access the value of TOptions for the lifetime of a request.
//
// Type parameters:
//   TOptions:
public interface IOptionsSnapshot<out TOptions> : IOptions<TOptions>
where TOptions : class, new()

会发现 IOptionsSnapshot 会针对每个单独的请求去重新读取一次配置,而 IOptions 则是第一次读取完以后就将对象保存在内存里了。

听说你想这样做?

网上搜索到的方法通常让你这样做:修改Program.cs,在CreateWebHostBuilder() 方法里加入这么一段:

...

WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
    config.SetBasePath(Directory.GetCurrentDirectory());
    config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
    config.AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true);
    config.AddEnvironmentVariables();
})

...

这段代码的作用是,让我们自己指定应用启动时加载哪些配置源,在这个案例里,我们依旧加载 appsettings.json 以及appsettings.Development.json。对于Json文件,有一个参数是reloadOnChange,表示是否在文件修改后,重新读取并加载到内存里,设为true

这段代码其实是没有必要的,这样改完代码还是得重启网站才能让设置生效,原因在强类型约束的时候使用的IOptions接口。

关于为什么显示指定reloadOnChange: true是没有必要的,可以做个简单测试:

我在appsettings.json里定义一个MySettings

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "MySettings": {
    "Message": ".NET Core Rocks!"
  },
  "AllowedHosts": "*"
}

建立对应的class

public class MySettings
{
    public string Message { get; set; }
}

注册服务

public void ConfigureServices(IServiceCollection services)
{
//...
 services.Configure<MySettings>(Configuration.GetSection(nameof(MySettings)));
}

依赖注入并输出结果

public class HomeController : Controller
{
    protected IConfiguration Configuration;
    protected MySettings MySettings { get; set; }
    public HomeController(
        IOptions<MySettings> settings = null, 
        IConfiguration configuration = null)
    {
        if (settings != null) MySettings = settings.Value;
        Configuration = configuration;
    }
    public IActionResult Index()
    {
 var m1 = MySettings.Message;
        var m2 = Configuration.GetSection("MySettings")["Message"];
        return Content($"m1:{m1}, m2:{m2}");
    }
}

这时候我并没有写 reloadOnChange: true,看看运行结果:

结果当然两者是一样的。然后我们在运行时热修改配置值

刷新网页,发现只有m2有变化。而这并不要求我显式指定reloadOnChange: true

要想让两者都取到最新的配置,使用上一节的方法,把m1的注入改成IOptionsSnapshot<MySettings>,现在再做热修改,两者都能立即生效:

破解谜团

刚才我们不指定reloadOnChange竟然也能做热修改,令人懵逼?我猜想,这个reloadOnChange,在最新版本的ASP.NET Core(2.2)中可能是默认启用的。关于这一点,我竟然没有找到官方的资料,不能确定这个参数的具体作用。但是ASP.NET Core是开源的,不妨来看看源代码:

代码位置:https://github.com/aspnet/Extensions

最终发现原来CreateDefaultBuilder()方法里,真的默认设置为reloadOnChange为true:

大家可以亲眼看看:

https://github.com/aspnet/Extensions/blob/master/src/Hosting/Hosting/src/Host.cs

好奇(注孤生)的程序员终于在折腾开源代码后得到了满足……

结论

ASP.NET Core 2.2 中如果要在运行时修改强类型配置,无需设置reloadOnChange = true,只需要使用IOptionsSnapshot接口即可大功告成!

本文分享自微信公众号 - 汪宇杰博客(ediwangblog)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-01-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏架构师

是时候开始学小程序开发了

在去年2017年1月9日,张小龙在2017微信公开课Pro上发布的小程序正式上线 。时至今日,小程序已经有整整一年时间了 。在2017年12月28日,微信更新的...

12030
来自专栏Jerry的SAP技术分享

when and where is getControllerName called

在开发js view时,需要实现一个方法getControllerName,并且正确返回该js view对应的controller name.

10420
来自专栏web前端教室

如何系统的学习web前端开发?

因为许多前端新人自学一段时间之后,就迷茫了,不知道学到了什么阶段,也不清楚接下来该往哪个方向学习。要知道前端开发的方向非常的多,相互之间的知识分叉也非常的多。

16730
来自专栏相约机器人

2019年最好的JavaScript图表库

随着数据收集和使用持续呈指数级增长,对这些数据进行可视化的需求变得越来越重要。开发人员寻求将数百万个数据库记录整合到美丽的图表和仪表板中,人类可以快速直观地解释...

44220
来自专栏Fundebug

小程序自定义单页面、全局导航栏

产品说小程序返回到首页不太方便,想添加返回首页按钮,UI说导航栏能不能设置背景图片,因为那样设计挺好看的。

468170
来自专栏小詹同学

爬虫神器!比selenium更高效!

介绍Pyppeteer之前先说一下Puppeteer,Puppeteer是谷歌出品的一款基于Node.js开发的一款工具,主要是用来操纵Chrome浏览器的 A...

31510
来自专栏相约机器人

可视化流式地理空间数据

最近参与了一个涉及流媒体信用卡交易数据并根据风险概率对其进行分类的项目。在此基础上,想探索可视化数据的选项。决定专注于地理方面,因为它是尝试识别欺诈性交易时的关...

17120
来自专栏Jerry的SAP技术分享

UI debug mode

launch pad line 91行有当前UI 运行mode的判断. 如果当前运行在non debug mode下,则line 110 动态加载core-mi...

9420
来自专栏Jerry的SAP技术分享

周期性取count请求是如何在前台setup的 - Tile count

在Chrome network tab里能够观测到每隔一个固定的时间间隔,前台会发起到后台的请求,读取最新的某transaction document的个数: ...

10420
来自专栏Jerry的SAP技术分享

UI5 navigation logic

UI5 view之间navigation的核心代码在folder resources/sap/ui/thirdparty里的js实现。

11930

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励