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接口即可大功告成!
總結
以上是生活随笔為你收集整理的ASP.NET Core 网站运行时修改设置如何自动生效的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微软热门开源项目及代码库地址
- 下一篇: 手写AspNetCore 认证授权代码