如何在 ASP.NET Core 中使用 Microsoft.Extensions.Configuration 讀取 appsettings.json 設定檔 如何在 ASP.NET Core 中使用 Microsoft.Extensions.Configuration 讀取 appsettings.json 設定檔

Published on Wednesday, June 7, 2023

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 容器來獲取這些設定值,而不需要在每個程式段落中都進行重複的讀取操作。

  1. 讀取實體文件可以使用 Microsoft.Extensions.FileProviders.Physical Nuget Package
  2. 解析 JSON 可以使用 System.Text.Json Nuget Package
  3. DI 容器使用 Microsoft.Extensions.DependencyInjection Nuget Package
  4. 要註冊到 DI 容器要先建立一組 interface 與 class 並且要考慮讀取時是否支援強型別或者轉換成字典並使用 index 來讀取

可以看得出來這幾個步驟很重要並且幾乎是每個專案都需要做,因此為了方便我們可以直接使用 Microsoft.Extensions.Configuration 相關的 package

  1. Microsoft.Extensions.Configuration
  2. Microsoft.Extensions.Configuration.Abstractions
  3. Microsoft.Extensions.Configuration.Binder
  4. Microsoft.Extensions.Configuration.EnvironmentVariables
  5. Microsoft.Extensions.Configuration.FileExtensions
  6. Microsoft.Extensions.Configuration.Ini
  7. Microsoft.Extensions.Configuration.Json
  8. Microsoft.Extensions.Configuration.UserSecrets
  9. Microsoft.Extensions.Configuration.Xml

整理後根據我們需要的功能安裝出以下三個 Package

  1. Microsoft.Extensions.Configuration.Binder
  2. Microsoft.Extensions.Configuration.Json
  3. Microsoft.Extensions.DependencyInjection

其中 Microsoft.Extensions.Configuration.Json 裡面有整合了 Configuration, FileProvidersSystem.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

最後透過 ConfigurationBuilderBuild 方法,可以獲得一個所有設定值的根節點 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 容器取得設定值


優化

我們目前是手動添加 JsonConfigurationSourceXmlConfigurationSource 這種方法比較不常見,推薦的作法是使用 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 所以很容易忽略這方面的知識,了解原理以後就能輕鬆的使用各種類型的設定檔了。