AppSettings
我們知道在 Dotnet 中有提供 appsettings.json
讓我們可以存放一些會變動的設定值,例如我們可以在 appsettings.json
設定
資料庫的連線字串之後就可以根據環境來選擇要連線到哪一個資料庫,今天來看一下跟 appsettings.json
相關的原始碼來學習 Dotnet 是怎麼處理
設定檔這方面的問題。
要了解這個問題可以建立一個 Console
專案,並且新增一個 appsettings.json
檔案
dotnet new console -o HelloConsole
touch appsettings.json
# appsettings.json
{
"JsonSettings": {
"From": "MyJsonSettings"
}
}
// HelloConsole.Program.cs
Console.WriteLine("Hello, World!");
為了在程式運行時讀取 appsettings.json
並能夠在程式任意段落讀取其中的設定值,我們可以按照以下步驟進行細分整理。
首先,我們需要實現讀取 appsettings.json
的功能,這表示我們的程式需要有能力讀取實體文件並解析 JSON。
其次,為了在任意程式段落中讀取 appsettings.json
中的設定值,我們可以使用依賴注入(Dependency Injection,簡稱 DI)的機制。
這樣我們可以將解析後的 appsettings.json
的設定值註冊為 Singleton(單例)到 DI 容器中。
這樣一來,我們就可以在程式的任意地方使用 DI 容器來獲取這些設定值,而不需要在每個程式段落中都進行重複的讀取操作。
- 讀取實體文件可以使用
Microsoft.Extensions.FileProviders.Physical
Nuget Package - 解析 JSON 可以使用
System.Text.Json
Nuget Package - DI 容器使用
Microsoft.Extensions.DependencyInjection
Nuget Package - 要註冊到 DI 容器要先建立一組 interface 與 class 並且要考慮讀取時是否支援強型別或者轉換成字典並使用 index 來讀取
可以看得出來這幾個步驟很重要並且幾乎是每個專案都需要做,因此為了方便我們可以直接使用 Microsoft.Extensions.Configuration
相關的 package
- Microsoft.Extensions.Configuration
- Microsoft.Extensions.Configuration.Abstractions
- Microsoft.Extensions.Configuration.Binder
- Microsoft.Extensions.Configuration.EnvironmentVariables
- Microsoft.Extensions.Configuration.FileExtensions
- Microsoft.Extensions.Configuration.Ini
- Microsoft.Extensions.Configuration.Json
- Microsoft.Extensions.Configuration.UserSecrets
- Microsoft.Extensions.Configuration.Xml
整理後根據我們需要的功能安裝出以下三個 Package
- Microsoft.Extensions.Configuration.Binder
- Microsoft.Extensions.Configuration.Json
- Microsoft.Extensions.DependencyInjection
其中 Microsoft.Extensions.Configuration.Json
裡面有整合了 Configuration
, FileProviders
與 System.Text.Json
因此我們安裝這個 Package 就好了,
Microsoft.Extensions.Configuration.Binder
則是用來將設定值映射到強行別的 Class 上。
回到我們的 HelloConsole 專案並安裝 Nuget Package
dotnet add package Microsoft.Extensions.Configuration.Binder --version 7.0.4
dotnet add package Microsoft.Extensions.Configuration.Json --version 7.0.0
dotnet add package Microsoft.Extensions.DependencyInjection --version 7.0.0
接下來需要先了解 Microsoft.Extensions.Configuration
提供的這幾個 Class
- ConfigurationBuilder
- ConfigurationProvider
- ConfigurationSource
- ConfigurationRoot
- ConfigurationSection
我們首先需要建立一個 ConfigurationBuilder
並使用多種不同的 ConfigurationSource
來建立對應的 ConfigurationProvider
。
例如可以使用 JsonConfigurationProvider
讀取 Json 格式的設定檔或者 XmlConfigurationProvider
讀取 Xml 格式的設定檔最後將設定匯總到 ConfigurationBuilder
。
最後透過 ConfigurationBuilder
的 Build
方法,可以獲得一個所有設定值的根節點 ConfigurationRoot
內部會根據設定檔建立多個
ConfigurationSection
以便區分不同的設定值。例如,我們可以建立一個區段來紀錄與連線字串相關的設定,另一個區段來紀錄與日誌相關的設定。
Json Configuration
根據上段整理的重點寫出以下測試程式
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder
.Add(new JsonConfigurationSource
{
FileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory()),
Path = "appsettings.json",
Optional = false,
ReloadOnChange = false,
});
var configurationRoot = configurationBuilder.Build();
Console.WriteLine(configurationRoot.GetSection("JsonSettings:From").Value);
// HelloConsole.csproj
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
運行之後確實可以讀取出設定值 MyJsonSettings
XML Configuration
我們可以再額外測試一下 XML 的設定檔
dotnet add package Microsoft.Extensions.Configuration.Xml --version 7.0.0
touch settings.xml
<?xml version="1.0"?>
<configuration>
<XmlSettings>
<From>MyXMLSettings</From>
</XmlSettings>
</configuration>
並且根據同樣的邏輯添加新的 XmlConfigurationSource
var configurationBuilder = new ConfigurationBuilder();
var physicalFileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());
configurationBuilder
.Add(new JsonConfigurationSource
{
FileProvider = physicalFileProvider,
Path = "appsettings.json",
Optional = false,
ReloadOnChange = false,
})
.Add(new XmlConfigurationSource
{
FileProvider = physicalFileProvider,
Path = "settings.xml",
Optional = false,
ReloadOnChange = false,
});
var configurationRoot = configurationBuilder.Build();
Console.WriteLine(configurationRoot.GetSection("JsonSettings:From").Value);
Console.WriteLine(configurationRoot.GetSection("XmlSettings:From").Value);
// HelloConsole.csproj
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="settings.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
# Shell 1
MyJsonSettings
MyXMLSettings
這樣的好處是儘管 Xml 與 Json 兩種檔案寫法不同但經過 ConfigurationBuilder
整理後得到的 ConfigurationRoot
卻可以使用同樣的 GetSection
方法獲取到檔案內的設定值。
Dependency Injection
我們已經取得關鍵的根節點 ConfigurationRoot
接下來需要將它註冊到 DI 容器內。
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(configurationBuilder.Build());
var serviceProvider = services.BuildServiceProvider();
var configuration = serviceProvider.GetService<IConfiguration>();
Console.WriteLine(configuration.GetSection("JsonSettings:From").Value);
Console.WriteLine(configuration.GetSection("XmlSettings:From").Value);
註冊為 Singleton 後以後就不需要重複讀取設定檔,可以直接到 DI 容器取得設定值
優化
我們目前是手動添加 JsonConfigurationSource
和 XmlConfigurationSource
這種方法比較不常見,推薦的作法是使用 Dotnet 封裝好的方法
可以讓程式碼更加簡潔以及有更好的可讀性。
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false)
.AddXmlFile("settings.xml", optional: false);
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(configurationBuilder.Build());
var serviceProvider = services.BuildServiceProvider();
var configuration = serviceProvider.GetService<IConfiguration>();
Console.WriteLine(configuration.GetSection("JsonSettings:From").Value);
Console.WriteLine(configuration.GetSection("XmlSettings:From").Value);
我們可以選擇安裝 Hosting
Package 來建立一個 Host,建立的時候會自動建立 DI 容器與呼叫 AddJsonFile 和註冊 IConfiguration,
這也是為什麼建立一個新的 Web 專案可以直接使用 appsettings.json
的原因。
dotnet add package Microsoft.Extensions.Hosting --version 7.0.1
var builder = Host.CreateDefaultBuilder();
builder.ConfigureAppConfiguration(builder1 => builder1.AddXmlFile("settings.xml", optional: false));
var app = builder.Build();
var configuration = app.Services.GetService<IConfiguration>();
Console.WriteLine(configuration.GetSection("JsonSettings:From").Value);
Console.WriteLine(configuration.GetSection("XmlSettings:From").Value);
app.Run();
Summary
今天學習了 Dotnet 內部是如何處理設定值的並且一步一步精簡程式碼,不過太精簡也容易造成誤會因為目前建立 web 專案時會自動處理
appsettings.json
所以很容易忽略這方面的知識,了解原理以後就能輕鬆的使用各種類型的設定檔了。