国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

ASP.NET Core —配置系統(tǒng)

這篇具有很好參考價(jià)值的文章主要介紹了ASP.NET Core —配置系統(tǒng)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

一個(gè)應(yīng)用要運(yùn)行起來,往往需要讀取很多的預(yù)設(shè)好的配置信息,根據(jù)約定好的信息或方式執(zhí)行一定的行為。

配置的本質(zhì)就是軟件運(yùn)行的參數(shù),在一個(gè)軟件實(shí)現(xiàn)中需要的參數(shù)非常多,如果我們以 Hard Code (硬編碼)的方式寫在應(yīng)用代碼中,這樣配置就會(huì)很亂,而且后續(xù)也不容易修改。亂而多,而且不容易修改,這就需要一個(gè)統(tǒng)一管理的地方,最常見的方式就是配置文件,這個(gè)也是開發(fā)人員非常熟悉的方式。

通過配置文件設(shè)置好軟件應(yīng)用運(yùn)行的各種參數(shù)之后,我們?cè)陂_發(fā)過程中需要能夠讀取到配置文件的內(nèi)容,根據(jù)配置內(nèi)容進(jìn)行軟件邏輯的判斷,實(shí)現(xiàn)完善的軟件行為邏輯。這一篇就是介紹 .NET Core 框架下怎么使用配置系統(tǒng),這也是 .NET Core 下的基礎(chǔ)設(shè)施之一。

1. 配置讀取

配置讀取是配置系統(tǒng)最基本的操作,幾乎是每個(gè)開發(fā)人員都會(huì)進(jìn)行的操作,一個(gè)開發(fā)人員可能不清楚配置系統(tǒng)是怎么實(shí)現(xiàn)的,配置文件是怎么解析的,但一定都做過讀取配置信息的操作。.NET Core 框架下對(duì)于配置系統(tǒng)的使用最終暴露出來的接口是 IConfiguration,它是供配置數(shù)據(jù)的統(tǒng)一視圖,配置讀取就通過這個(gè)接口的實(shí)現(xiàn)來進(jìn)行。

默認(rèn)創(chuàng)建的 ASP .NET Core 框架模板項(xiàng)目中默認(rèn)有一個(gè)appsettings.json 配置文件,這個(gè)也是 ASP.NET Core 中最常用的配置文件,在配置文件中添加多一個(gè) Settings 節(jié)點(diǎn),內(nèi)容如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Settings": {
    "key1": "value1",
    "key2": 1,
    "key3": true,
    "key4": {
      "subKey1": "value",
      "subKey2": 1
    },
    "items": [ "item1", "item2", "item3" ]
  }
}

我們要讀取配置文件中的內(nèi)容,例如讀取 “AllowedHosts” 對(duì)于的值,只需要將其注入到需要的服務(wù)類中即可使用,ASP.NET Core 模板項(xiàng)目中使用 Web 主機(jī)構(gòu)建和管理應(yīng)用,在使用主機(jī)默認(rèn)配置的時(shí)候已經(jīng)將 IConfiguration 服務(wù)注冊(cè)到依賴注入容器之中。

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly IConfiguration _configuration;
    public WeatherForecastController(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    [HttpGet]
    public Task Get()
    {
        var allowedHosts = _configuration["AllowedHosts"];
        Console.WriteLine(allowedHosts);
		// 配置鍵不區(qū)分大小寫
		var allowedHosts = _configuration["AllowedHosts"];
		Console.WriteLine(allowedHosts);
        return Task.CompletedTask;
    }
}

上面這種讀取方式是索引器方式,最簡(jiǎn)單也是基本的方式,配置被加載到內(nèi)存中是以鍵值對(duì)的方式存在的,我們可以通過配置鍵讀取配置值,鍵是字符串,不區(qū)分大小寫,讀取出來的值都是字符串。

配置文件中配置值往往不止一層,就像上面 appsettings.json 文件中,Logging 節(jié)點(diǎn)下還有子節(jié)點(diǎn),如果需要這種分層數(shù)據(jù),可以使用 : 字符(英文冒號(hào))分隔層次結(jié)構(gòu),例如獲取上面配置鍵 Default 對(duì)于的配置值。

// 以 : 作為分隔符,表示層級(jí)結(jié)構(gòu)
var defalutLogLevel = _configuration["Logging:LogLevel:Default"];

如果配置值是數(shù)組,需要讀取數(shù)組中具體的某一個(gè)值,可以用該值在數(shù)組中的索引作為 key,例如讀取上面配置文件中的 items 數(shù)組中的 item2。

// 讀取數(shù)組,可以用值在數(shù)組中的索引作為key
var item2 = _configuration["Settings:items:1"];

這種方式讀取配置有挺多不方便的地方,例如配置值是數(shù)值型的時(shí)候,需要我們直接轉(zhuǎn)換,例如一次只能讀取到一個(gè)配置值。微軟通過 Microsoft.Extensions.Configuration.Binder 中的 ConfigurationBinder類提供了一些 IConfiguration 的靜態(tài)方法,用于獲取配置值時(shí)進(jìn)行自動(dòng)轉(zhuǎn)換和綁定。

(1) GetValue

通過 ConfigurationBinder 中的 GetValue 擴(kuò)展方法,一樣可以通過配置鍵從配置系統(tǒng)中讀取對(duì)于的配置值。該方法有多個(gè)重載,支持通過泛型的方式進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換,并且支持設(shè)置默認(rèn)值。

var defaultLogLevel2 = _configuration.GetValue<string>("Logging:LogLevel:Default");
// 配置信息中不包含 "Logging:LogLevel:Default" 這個(gè)Key時(shí),以默認(rèn)值 "Error" 返回
var defaultLogLevel3 = _configuration.GetValue<string>("Logging:LogLevel:Default", "Error");

(2) GetSection

這樣子有些情況下仍然無法滿足我們的需要,某一些情況下我們會(huì)需要直接讀取配置中的一部分節(jié)點(diǎn),例如直接讀取上面配置中的 LogLevel 部分。IConfiguration 中的 GetSection 方法可以通過 Key 直接讀取某一個(gè)子節(jié)點(diǎn)。該方法的返回值是 IConfigurationSection 類型,永遠(yuǎn)不會(huì)返回null,IConfigurationSection 實(shí)際上是一個(gè) IConfiguration 的派生接口,也就是說我們還可以從 IConfigurationSection 再去獲取我們需要的具體的配置值。

var section = _configuration.GetSection("Settings:key4");
var defaultLogLevel4 = section["Default"];

(3) Get

上面講到通過 GetSection 獲取到了配置文件中的一部分子節(jié)點(diǎn),但是那樣仍然不方便,還是需要一個(gè)一個(gè)去讀取具體的值??梢酝ㄟ^ ConfigurationBinder.Get 擴(kuò)展方法,將配置以強(qiáng)類型的方式綁定到對(duì)象上。

首先需要定義一個(gè)類來接收配置文件中的節(jié)點(diǎn)信息

public class KeyOptions
{
    public string subKey1 { get; set; }

    public int subKey2 { get; set; }
}

然后通過以下方式進(jìn)行綁定:

var keyOption1 = _configuration.GetSection("Settings:key4").Get<KeyOptions>();

(4) Bind

ConfigurationBinder.Bind 擴(kuò)展方法與 Get 方法類似,也是用于將配置綁定為強(qiáng)類型對(duì)象,不過 Bind的 方法是綁定到一個(gè)已實(shí)例化的對(duì)象上,需要提供一個(gè)已存在的對(duì)象。

var keyOption2 = new KeyOptions();
_configuration.GetSection("Settings:key4").Bind(keyOption2);

(5) Exists

上面說過,GetSection 方法獲取配置中的子節(jié)點(diǎn),返回值永遠(yuǎn)不會(huì)為 null。如果我們傳入了一個(gè)不存在的 key,肯定是獲取不到對(duì)于的值的,這種情況下還是需要判斷對(duì)于的子節(jié)點(diǎn)到底是不是真正存在的,這時(shí)候可以使用 Exists 方法。

var section = _configuration.GetSection("settings");
var exist = section.Exists();

除此之外,還有一個(gè) GetChildren 方法,無需參數(shù),用于獲取到當(dāng)前配置節(jié)點(diǎn)的所有直接子節(jié)點(diǎn)的集合。

以上就是 .NET Core 體系下配置系統(tǒng)讀取配置的基本介紹,涉及到的類型最主要的是 IConfiguration 接口,除此之外還有上面提到的 IConfigurationSection 接口,以及 IConfigurationRoot 接口。

IConfigurationRoot 表示配置的根節(jié)點(diǎn),是 IConfiguration 的派生接口,以下為接口的定義:

public interface IConfigurationRoot: IConfiguration{
	// 存放了當(dāng)前應(yīng)用程序的所有配置提供程序
	IEnumerable<IConfigurationProvider> Providers { get; }
	// 強(qiáng)制從配置提供程序中重載配置
	void Reload();
}

這里可以看到一個(gè)關(guān)鍵的屬性 IEnumerable<IConfigurationProvider> Providers,這個(gè)就是配置系統(tǒng)中的配置信息的來源,后面會(huì)仔細(xì)講這個(gè)。而 Reload 方法中最關(guān)鍵的也是調(diào)用集合中各個(gè) IConfigurationProvider 進(jìn)行數(shù)據(jù)加載。

IConfigurationSection 表示配置中的子節(jié)點(diǎn),也是 IConfiguration 的派生接口,以下為接口的定義:

public interface IConfigurationSection: IConfiguration{
	// 該子節(jié)點(diǎn)在其父節(jié)點(diǎn)中所表示的 key,即直接對(duì)應(yīng)的key
	string Key { get; }
	// 該子節(jié)點(diǎn)在配置中的全路徑(從根節(jié)點(diǎn)開始,到當(dāng)前節(jié)點(diǎn)以:符號(hào)分隔的路徑)
	string Path { get; }
	// 該子節(jié)點(diǎn)的 value。如果該子節(jié)點(diǎn)是葉子節(jié)點(diǎn),則Value為該節(jié)點(diǎn)對(duì)于的值,若其下存在子節(jié)點(diǎn),則其始終為 null
	string Value { get; set; }
}

IConfigurationSection 接口通過以上三個(gè)屬性,結(jié)合IConfiguration中的 GetChildren 方法來完整地表示的一個(gè)子節(jié)點(diǎn),而 Exists 方法判斷節(jié)點(diǎn)是否為空,就是針對(duì) IConfigurationSection 中的Value屬性和 GetChildren 方法來進(jìn)行的。

public static class ConfigurationExtensions{
	public static bool Exists(thisIConfigurationSection section){
	    if(section == null)
	    {
	        returnfalse;
	    }
	    returnsection.Value != null || section.GetChildren().Any();
	}
}

2. 配置添加

配置系統(tǒng)可以讀取到配置文件中的信息,那必然有某個(gè)地方可以將配置文件添加到配置系統(tǒng)中。之前的文章中講到ASP.NET Core 入口文件中,builder(WebApplicationBuilder 對(duì)象) 中有一個(gè) Configuration 屬性,這里就是我們擴(kuò)展添加額外的配置的地方。

查看 Configuration 屬性,可以看到是 ConfigurationManager 類型,而 ConfigurationManager 實(shí)現(xiàn)了 IConfigurationBuilder 接口。
ASP.NET Core —配置系統(tǒng)
ASP.NET Core —配置系統(tǒng)
IConfigurationBuilder 接口定義如下:

public interface IConfigurationBuilder{
       // 存放用于該 Builder 的 Sources 列表中各個(gè)元素的共享字典
	IDictionary<string, object> Properties { get; }
	// 已注冊(cè)的 IConfigurationSource 列表
	IList<IConfigurationSource> Sources { get; }
	// 將 IConfigurationSource 添加到 Sources 中
	IConfigurationBuilder Add(IConfigurationSource source);
	// 通過 Sources 構(gòu)建配置提供程序?qū)嵗?,并?chuàng)建 IConfigurationRoot 實(shí)例
	IConfigurationRoot Build();
}

而它的實(shí)現(xiàn)類 ConfigurationBuilder 就是配置系統(tǒng)的入口。

public class ConfigurationBuilder : IConfigurationBuilder
{
    public IList<IConfigurationSource> Sources { get; } = new List<IConfigurationSource>();

    public IDictionary<string, object> Properties { get; } = new Dictionary<string, object>();

    public IConfigurationBuilder Add(IConfigurationSource source)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        Sources.Add(source);
        return this;
    }

    public IConfigurationRoot Build()
    {
        var providers = new List<IConfigurationProvider>();
        foreach (IConfigurationSource source in Sources)
        {
            IConfigurationProvider provider = source.Build(this);
            providers.Add(provider);
        }
        return new ConfigurationRoot(providers);
    }
}

當(dāng)我們啟動(dòng)一個(gè) ASP.NET Core 應(yīng)用的時(shí)候是創(chuàng)建并啟動(dòng)了一個(gè) Web 主機(jī),由 Web 主機(jī)來啟動(dòng)并管理我們的應(yīng)用的生命周期,在這個(gè)過程中會(huì)默認(rèn)添加一些配置提供程序,加載一些配置信息。這些操作就在以下代碼中:

var builder = WebApplication.CreateBuilder(args);

.NET Core 框架下的主機(jī)除了適用于 Web 應(yīng)用的 Web 主機(jī)之外,還有通用主機(jī)。若是在普通的控制臺(tái)應(yīng)用程序,想要通過主機(jī)啟動(dòng)應(yīng)用,并使用配置系統(tǒng)可用以下方式:

(1) 添加 Microsoft.Extensions.Hosting Nuget 包
(2) 通過以下代碼創(chuàng)建主機(jī)

using IHost host = Host.CreateDefaultBuilder(args).Build();
host.Run();

主機(jī)這塊就先稍微了解以下,后面還會(huì)有專門的文章去介紹。除了通過主機(jī)的方式使用配置系統(tǒng)之外,我們也可以直接通過 ConfigurationBuilder 類構(gòu)建,如下:

IConfiguration config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();

config.GetValue<string>("Logging:LogLevel:Default");

這里需要引入相應(yīng)的 Nuget 包,例如使用 ConfigurationBinder 相關(guān)的擴(kuò)展方法就要引入 Microsoft.Extensions.Configuration.Binder Nuget包,使用 Json 格式配置文件就要引入Microsoft.Extensions.Configuration.Json Nuget包,該Nuget 包中包含 AddJsonFile 擴(kuò)展方法,通過指定文件路徑添加相應(yīng)的配置文件到配置系統(tǒng)中。

配置文件多種多樣,比較常用的都有 Json 、xml、ini、yaml 等多種,甚至配置信息不一定存儲(chǔ)在文件之中。.NET Core 配置系統(tǒng)對(duì)各種來源的配置信息進(jìn)行了抽象,不同來源只要提供相應(yīng)的配置提供程序即可,也就是我們上面在 IConfigurationRoot 接口中看到的 IConfigurationProvider 接口的實(shí)現(xiàn)類。配置提供程序內(nèi)部對(duì)不同來源不同格式的配置信息進(jìn)行加載、刷新,并提供統(tǒng)一的訪問方式,也就是鍵值對(duì),實(shí)際上所有的配置信息最終會(huì)以鍵值對(duì)的方式被讀取到內(nèi)存中的 Dictionary 對(duì)象中。

我們要添加不同類型不同來源的配置信息,只需要通過 IConfigurationBuilder 實(shí)現(xiàn)類對(duì)象添加不同的配置提供程序即可。在 ASP.NET Core 應(yīng)用之中,可以通過以下方式進(jìn)行添加:

// 添加一個(gè)xml配置文件,并加入到配置系統(tǒng)中
var configirationFilePath = Path.Combine(Directory.GetCurrentDirectory(), "xxx.xml");
builder.Configuration.AddXmlFile(configirationFilePath);

也可以通過以下方式:

builder.WebHost.ConfigureAppConfiguration(builder =>
{
    builder.AddXmlFile("");
});

這兩種方式是一樣的效果的,只不過具體的實(shí)現(xiàn)類不同,ConfigureAppConfiguration 可以將原有的配置提供程序情況,而 builder.Configuration 則不行,只能往集合后面繼續(xù)添加。

3. 配置提供程序

上面提到,通過 IConfigurationBuilder 的實(shí)現(xiàn)類對(duì)象,我們可以自由地往配置系統(tǒng)中添加不同的配置提供程序,從而獲取不同來源的配置信息。.NET Core 中,微軟提供了以下這些內(nèi)置的配置提供程序:

○ 文件配置提供程序
○ 環(huán)境變量配置提供程序
○ 命令行配置提供程序
○ Azure應(yīng)用配置提供程序
○ Azure Key Vault 配置提供程序
○ Key-per-file配置提供程序
○ 內(nèi)存配置提供程序
○ 應(yīng)用機(jī)密(機(jī)密管理器)
○ 自定義配置提供程序

這里稍微介紹一下常用的幾個(gè)。

3.1 文件配置提供程序

顧名思義,這個(gè)就是我們熟悉的配置加載方式,從配置文件中加載配置信息。配置文件多種多樣,.NET Core 框架內(nèi)置支持 Json、xml、ini 三種格式的文件提供程序:

  • JSON配置提供程序(JsonConfigurationProvider)
  • XML配置提供程序(XmlConfigurationProvider)
  • INI配置提供程序(IniConfigurationProvider)

以上這些配置提供程序,均繼承于抽象基類 FileConfigurationProvider,當(dāng)一個(gè)提供程序中發(fā)現(xiàn)重復(fù)的鍵時(shí),提供程序會(huì)引發(fā) FormatException,所有類型的文件提供程序都是這樣的機(jī)制。

另外,所有文件配置提供程序都支持提供兩個(gè)配置參數(shù):

  • optional:bool 類型,指示該文件是否是可選的。如果該參數(shù)為false,但是指定的文件又不存在,則會(huì)報(bào)錯(cuò)。
  • reloadOnChange:bool 類型,指示該文件發(fā)生更改時(shí),是否要重新加載配置。

3.1.1 JSON配置提供程序

JSON 配置提供程序被封裝在 Microsoft.Extensions.Configuration.Json Nuget包中,若通過 ConfigurationBuilder 自行構(gòu)建配置系統(tǒng)需要先安裝該依賴包。它通過 JsonConfigurationProvider 在運(yùn)行時(shí)從 Json 文件中加載配置。

使用方式非常簡(jiǎn)單,通過 IConfigurationBuilder 的實(shí)現(xiàn)類對(duì)象調(diào)用 AddJsonFile 擴(kuò)展方法指定Json配置文件的路徑即可。以下代碼可用于控制臺(tái)程序中創(chuàng)建主機(jī)并設(shè)置配置系統(tǒng):

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        var env = context.HostingEnvironment;
        // 添加 json 配置文件
        config.AddJsonFile("appsettings.json",true, true)
            .AddJsonFile($"appsetting.{env.EnvironmentName}.json", true, true);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");

host.Run();

appsetting.json 配置文件中的內(nèi)容如下:

{
  "Settings": {
    "Provider": "JsonProvider",
    "version": {
      "subKey1": "value",
      "subKey2": 1
    },
    "items": [ "item1", "item2", "item3" ]
  }
}

控制臺(tái)程序運(yùn)行之后輸出如下:
ASP.NET Core —配置系統(tǒng)
這樣有一點(diǎn)要注意的是,對(duì)于我們手動(dòng)添加的配置文件需要設(shè)置一下文件屬性,讓其在項(xiàng)目生成的時(shí)候能夠正常生成到運(yùn)行目錄,確保應(yīng)用可以正常獲取到該文件:

ASP.NET Core —配置系統(tǒng)

3.1.2 XML配置提供程序

XML 配置提供程序被封裝在 Microsoft.Extensions.Configuration.Xml Nuget包中,通過 XmlConfigurationProvider 類在運(yùn)行時(shí)從 XML 文件加載配置。

使用方式也很簡(jiǎn)單,與 JSON 配置提供程序類似,通過 AddXmlFile 擴(kuò)展方法指定配置文件路徑。

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        var env = context.HostingEnvironment;
         添加 json 配置文件
        //config.AddJsonFile("appsettings.json",true, true)
        //    .AddJsonFile($"appsetting.{env.EnvironmentName}.json", true, true);

        config.AddXmlFile("appsettings.xml", true, true);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

xml 配置文件內(nèi)容如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<Settings>
		<Provider>XmlProvider</Provider>
		<version>
			<subKey1>value</subKey1>
			<subKey2>1</subKey2>
		</version>
		<items>item1</items>
		<items>item2</items>
		<items>item3</items>
	</Settings>
</configuration>

運(yùn)行程序控制臺(tái)輸出如下:
ASP.NET Core —配置系統(tǒng)
這里有一個(gè)和版本有關(guān)的點(diǎn),對(duì) Xml 文件中使用同一元素名稱的重復(fù)元素,一般也就是數(shù)組,.NET 6及之后的xml 配置提供程序會(huì)自動(dòng)為其編制索引,不再需要顯式指定name屬性。如果是 .NET 6 以下的版本則需要這樣了:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<Settings>
		<Provider>XmlProvider</Provider>
		<version>
			<subKey1>value</subKey1>
			<subKey2>1</subKey2>
		</version>
		<items name="itemkey1">item1</items>
		<items name="itemkey2">item2</items>
		<items name="itemkey3">item3</items>
	</Settings>
</configuration>
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:itemkey2")}");

另外 xml 文件中的屬性也可用于提供值:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <key attribute="value" />
  <section>
    <key attribute="value" />
  </section>
</configuration>

獲取屬性的值可用以下配置鍵:

key:attribute
section:key:attribute

3.1.3 INI配置提供程序

INI 配置提供程序被封裝在 Microsoft.Extensions.Configuration.Ini Nuget包,通過 IniConfigurationProvider 類在運(yùn)行時(shí)從 INI 文件加載配置。使用方式如下:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        config.AddIniFile("appsettings.ini", true, true);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

ini 配置文件內(nèi)容如下:

[Settings]
Provider="IniProvider"
version:subKey1="value"
version:subKey2=1
items:0="item1"
items:1="item2"
items:3="item3"

運(yùn)行應(yīng)用,控制臺(tái)輸出如下:

ASP.NET Core —配置系統(tǒng)

3.2 環(huán)境變量配置提供程序

環(huán)境變量配置提供程序被封裝在 Microsoft.Extensions.Configuration.EnvironmentVariables, 通過 EnvironmentVariablesConfigurationProvider 在運(yùn)行時(shí)從環(huán)境變量中以鍵值對(duì)的方式加載配置。

環(huán)境變量一般情況下是配置在機(jī)器上的,而不同的操作系統(tǒng)對(duì)環(huán)境變量的設(shè)置要求有所不同,當(dāng)環(huán)境變量存在多層的時(shí)候,層級(jí)之間的分隔有些支持通過 : 號(hào)進(jìn)行分隔,有些不支持,雙下劃線 __ 是全平臺(tái)支持的,所以設(shè)置環(huán)境變量的時(shí)候要使用雙下劃線 __ 來代替冒號(hào) :

各種不同的平臺(tái)下怎么去添加環(huán)境變量這里就不細(xì)說了,Windows 下大家最起碼都應(yīng)該知道可以通過 我的電腦 -> 屬性 -> 高級(jí)系統(tǒng)設(shè)置 去可視化的添加,命令行的方式可閱讀下官方文章: ASP.NET Core 中的配置 | Microsoft Learn,Linux 平臺(tái)下可以通過 export 命令臨時(shí)添加,或者修改相應(yīng)的配置文件 ~/.bashrc/etc/profile,大家仔細(xì)查一下資料就行了。

處理在機(jī)器上直接設(shè)置環(huán)境變量外,我們開發(fā)測(cè)試的過程中也可以通過 ASP.NET Core 框架下的 launchSettings.json 配置文件設(shè)置用于調(diào)試的臨時(shí)環(huán)境變量。在應(yīng)用啟動(dòng)調(diào)試時(shí),該文件中的環(huán)境變量會(huì)替代系統(tǒng)的中的環(huán)境變量。

{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "profiles": {
    "ConfigurationSample": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "launchUrl": "swagger",
      "applicationUrl": "http://localhost:5004",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "Custom_settings__Provider": "EnvironmentVariablesProvider",
        "Custom_settings__version__subKey1": "value",
        "Custom_settings__items__0": "item1",
        "Custom_settings__items__1": "item2",
        "Custom_settings__items__2": "item3"
      }
    }
  }
}

環(huán)境變量配置提供程序使用也很簡(jiǎn)單,注意以下示例為了使用 launchSettings.json 中的環(huán)境變量是在 ASP.NET Core 項(xiàng)目中測(cè)試的。

var builder = WebApplication.CreateBuilder(args);

builder.Host.ConfigureAppConfiguration(builder =>
{
    builder.Sources.Clear();
    // 篩選前置為 Custom_ 的環(huán)境變量,將其加載為應(yīng)用配置,其他的不加載
    builder.AddEnvironmentVariables("Custom_");
});

var app = builder.Build();

Console.WriteLine($"Settings:Provider: {app.Configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {app.Configuration.GetValue<string>("Settings:items:1")}");

app.Run();

在添加環(huán)境變量時(shí),通過指定參數(shù) prefix,只讀取限定前綴的環(huán)境變量。不過在讀取環(huán)境變量時(shí),會(huì)將前綴刪除。如果不指定參數(shù) prefix,那么會(huì)讀取所有環(huán)境變量。

當(dāng)創(chuàng)建默認(rèn)通用主機(jī)(Host)時(shí),默認(rèn)就已經(jīng)添加了前綴為DOTNET_的環(huán)境變量,如果是在 ASP.NET Core 中,配置了 Web 主機(jī)時(shí),默認(rèn)添加了前綴為 ASPNETCORE_ 的環(huán)境變量,而后主機(jī)加載應(yīng)用配置時(shí),再根據(jù)策略添加了其他的環(huán)境變量,如果沒有傳遞 prefix 參數(shù)則是所有環(huán)境變量。這一塊的加載機(jī)制,下面再細(xì)講。

運(yùn)行應(yīng)用,控制臺(tái)輸出如下:
ASP.NET Core —配置系統(tǒng)
除此之外,環(huán)境變量提供程序還有一些隱藏的功能點(diǎn),當(dāng)沒有向 AddEnvironmentVariables 傳入前綴時(shí),默認(rèn)也會(huì)針對(duì)含有以下前綴的環(huán)境變量進(jìn)行特殊處理:
ASP.NET Core —配置系統(tǒng)
這個(gè)功能點(diǎn)比較少用到,但是大家看到這個(gè)大概都會(huì)有點(diǎn)疑惑,具體的形式是怎么樣的,下面稍微測(cè)試一下

首先在 launchSettings.json 文件中添加多一個(gè)環(huán)境變量:

"MYSQLCONNSTR_Default": "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;"

之后在應(yīng)用中打印如下兩個(gè)配置:

Console.WriteLine($"ConnectionStrings:Default: { app.Configuration.GetValue<string>("ConnectionStrings:Default") }");
Console.WriteLine($"ConnectionStrings:Default_Provider: { app.Configuration.GetValue<string>("ConnectionStrings:Default_ProviderName") }");

輸出結(jié)果如下:
ASP.NET Core —配置系統(tǒng)
也就是說,這種形式的環(huán)境變量會(huì)被自動(dòng)轉(zhuǎn)換為兩個(gè)。

3.3 命令行配置提供程序

命令行配置提供程序被封裝在 Microsoft.Extensions.Configuration.CommandLine 包中,通過 CommandLineConfigurationProvider 在運(yùn)行時(shí)從命令行參數(shù)鍵值對(duì)中加載配置。

當(dāng)我們通過 dotnet 命令啟動(dòng)一個(gè) .NET Core 應(yīng)用時(shí),我們可以在命令后面追加一些參數(shù),這些參數(shù)將在入口文件中被 args 變量接收到。命令行配置提供程序使用如下:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        config.AddCommandLine(args);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

之后通過命令行程序啟動(dòng)應(yīng)用,并傳入相應(yīng)的參數(shù):

dotnet ConfigurationSampleConsole.dll Settings:Provider=CommandLineProvider Settings:items:1=item1

ASP.NET Core —配置系統(tǒng)
命令行參數(shù)的設(shè)置有三種方式:

(1) 使用 = 號(hào)連接鍵值:

dotnet ConfigurationSampleConsole.dll Settings:Provider=CommandLineProvider Settings:items:0=item1 Settings:items:1=item2

(2) 使用 / 號(hào)表示鍵,值跟在鍵后面,鍵值以空格分隔

dotnet ConfigurationSampleConsole.dll /Settings:Provider CommandLineProvider /Settings:items:0 item1 /Settings:items:1 item2

(3) 使用 – 符號(hào)表示鍵,值跟在鍵后面,鍵值以空格分隔

dotnet ConfigurationSampleConsole.dll --Settings:Provider CommandLineProvider --Settings:items:0 item1 --Settings:items:1 item2

如果值之中本來就有空格的,可以使用 “” 號(hào)包括。

dotnet ConfigurationSampleConsole.dll --Settings:Provider CommandLineProvider --Settings:items:0 item1 --Settings:items:1 "test item2"

AddCommandLine 擴(kuò)展方法提供了重載,允許額外傳入一個(gè)參數(shù),該參數(shù)提供一個(gè)交換映射字典,針對(duì)命令行配置參數(shù)進(jìn)行key映射。例如命令行傳入鍵是 name01 ,映射后的的鍵為 project:name。這里有一些要注意的點(diǎn):

  • 交換映射key必須以---開頭。當(dāng)使用-開頭時(shí),命令行參數(shù)書寫時(shí)也要以-開頭,當(dāng)使用--開頭時(shí),命令行參數(shù)書寫時(shí)可以以--/開頭。
  • 交換映射字典中的 key 不區(qū)分大小寫,不能包含重復(fù) key。如不能同時(shí)出現(xiàn)-n-N,但可以同時(shí)出現(xiàn)-n--n
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        var switchMappings = new Dictionary<string, string>
        {
            ["--b1"] = "Settings:Provider",
            ["-b2"] = "Settings:items"
        };

        config.AddCommandLine(args, switchMappings);
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

ASP.NET Core —配置系統(tǒng)

3.4 內(nèi)存配置提供程序

內(nèi)存配置提供程序就比較簡(jiǎn)單了,它直接被包含在 Microsoft.Extensions.Configuration,通過 MemoryConfigurationProvider 在運(yùn)行時(shí)從內(nèi)存中的集合中加載配置。使用方式如下:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        config.AddInMemoryCollection(new Dictionary<string, string> {
            { "Settings:Provider", "InMemoryProvider" },
            { "Settings:items:1", "MemoryItem" }
        });
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:items[1]: {configuration.GetValue<string>("Settings:items:1")}");

host.Run();

ASP.NET Core —配置系統(tǒng)

3.5 配置加載順序

上面介紹了一些常用的配置提供程序,這些配置提供程序都是通過擴(kuò)展方法添加到 ConfigurationBuilder 對(duì)象中的,而從上面 ConfigurationBuilder 的源碼可以看出,添加一個(gè)配置提供程序的時(shí)候其實(shí)應(yīng)該是添加了一個(gè)對(duì)應(yīng)的 IConfigurationSource 對(duì)象,而后在 ConfigurationBuilder 中被保存到集合中。

這就可以看出,配置系統(tǒng)是允許同時(shí)添加多種配置提供程序,支持多來源的配置信息同時(shí)存在的。那么當(dāng)多個(gè)配置處理程序都被添加到配置系統(tǒng)之中,那我們從配置系統(tǒng)中通過配置鍵獲取配置值的時(shí)候是怎么進(jìn)行的呢,當(dāng)多個(gè)配置提供程序存在相同的配置鍵時(shí),我們獲取到的配置值是哪個(gè)呢?

從 ConfigurationRoot 的源碼中可以可以看到,當(dāng)我們用索引器API讀取配置值時(shí),是調(diào)用了 GetConfiguration 方法

ASP.NET Core —配置系統(tǒng)
而GetConfiguration方法中的邏輯也很簡(jiǎn)單,只是遍歷提供程序集合嘗試從提供程序去獲取值,需要關(guān)注的是遍歷的順序。
ASP.NET Core —配置系統(tǒng)
這里的邏輯是這樣子的,倒敘進(jìn)行遍歷,后添加的配置處理程序先被遍歷,一旦通過key從提供程序中獲取到值就返回結(jié)果,不再繼續(xù)遍歷。所以添加配置提供程序的順序決定相同配置鍵最終的值, 當(dāng)多個(gè)配置處理程序存在相同鍵時(shí),越后添加的配置提供程序優(yōu)先級(jí)越高,從最后的一個(gè)提供程序獲取到值之后就不再從其他處理程序獲取。

3.6 默認(rèn)配置來源

上面也有提到通過主機(jī)運(yùn)行和管理應(yīng)用,在通過主機(jī)運(yùn)行的項(xiàng)目中,主機(jī)在啟動(dòng)的時(shí)候就已經(jīng)默認(rèn)添加了一些配置提供程序,所以我們創(chuàng)建了一個(gè) ASP.NET Core 模板項(xiàng)目之后就可以獲取到 appsettings.json 等配置文件中的配置信息。下面介紹一下默認(rèn)添加的配置提供程序。

在 Host.CreateDefaultBuilder(String[]) 方法或者 WebApplication.CreateBuilder(args) 方法執(zhí)行的時(shí)候,會(huì)按照以下順序添加應(yīng)用的配置提供程序:

(1) 內(nèi)存配置提供程序
(2) Chained 配置提供程序(添加現(xiàn)有的主機(jī)配置)
(3) JSON 配置提供程序 (添加 appsettings.json 配置文件)
(4) JSON 配置提供程序 (添加 appsettings.{Environment}.json 配置文件)
(5) 機(jī)密管理器(僅Windows)
(6) 環(huán)境變量配置提供程序 (未限定前綴)
(7) 命令行配置提供程序

配置分主機(jī)配置和應(yīng)用配置,主機(jī)啟動(dòng)時(shí)應(yīng)用仍未啟動(dòng),主機(jī)啟動(dòng)過程中的配置就是主機(jī)配置。上面第一個(gè)Chained 配置提供程序就是承接過來的主機(jī)配置。而主機(jī)配置是按照以下順序加載的:

(1) 環(huán)境變量配置提供程序(以 DOTNET_ 為前綴的環(huán)境變量)
(2) 命令行配置提供程序 (命令行參數(shù))
(3) 環(huán)境變量配置提供程序(以 ASPNETCORE_ 為前綴的環(huán)境變量,如果是Web主機(jī)的話)

所以最終的應(yīng)用配置加載順序應(yīng)該是下面這樣:

(1) 內(nèi)存配置提供程序
(2) 環(huán)境變量配置提供程序(以 DOTNET_ 為前綴的環(huán)境變量)
(3) 命令行配置提供程序 (命令行參數(shù))
(4) 環(huán)境變量配置提供程序(以 ASPNETCORE_ 為前綴的環(huán)境變量,如果是Web主機(jī)的話)
(5) JSON 配置提供程序 (添加 appsettings.json 配置文件)
(6) JSON 配置提供程序 (添加 appsettings.{Environment}.json 配置文件)
(7) 機(jī)密管理器(僅Windows)
(8) 環(huán)境變量配置提供程序 (未限定前綴)
(9) 命令行配置提供程序 (命令行參數(shù))

按照越后面添加的提供程序優(yōu)先的方式,最終應(yīng)用配置會(huì)覆蓋主機(jī)配置,并且最優(yōu)先是最后添加的命令行配置提供程序,我們可以通過以下方式打印配置系統(tǒng)中所有的配置提供程序,進(jìn)行驗(yàn)證:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

var configurationRoot = (IConfigurationRoot)app.Configuration;
foreach (var provider in configurationRoot.Providers.AsEnumerable())
{
    Console.WriteLine(provider.ToString());
}

app.Run();

最終控制臺(tái)打印出來的結(jié)果如下:
ASP.NET Core —配置系統(tǒng)
雖然應(yīng)用配置優(yōu)先,會(huì)覆蓋前面的主機(jī)配置,但是有一些變量會(huì)在初始化主機(jī)生成器的時(shí)候就提前進(jìn)行鎖定,并且之后不會(huì)受應(yīng)用配置的影響:

  • 應(yīng)用程序名稱
  • 環(huán)境名稱,例如 Development、Production 和 Staging
  • 內(nèi)容根目錄
  • Web 根目錄
  • 是否要掃描托管啟動(dòng)程序集以及要掃描哪些程序集。
  • 應(yīng)用和庫代碼從 IHostBuilder.ConfigureAppConfiguration 回調(diào)中的 HostBuilderContext.Configuration 讀取的變量。

這里提到環(huán)境名稱,其實(shí)也就是軟件運(yùn)行的環(huán)境,最最基本的也會(huì)分為開發(fā)環(huán)境、生產(chǎn)環(huán)境兩種。軟件運(yùn)行環(huán)境通過環(huán)境變量來設(shè)置,普通的 .NET Core 應(yīng)用環(huán)境變量 key 為NETCORE_ENVIRONMENT,Web 應(yīng)用環(huán)境變量 key 為ASPNETCORE_ENVIRONMENT,Web 應(yīng)用下如果兩者同時(shí)存在,后者會(huì)覆蓋前者。軟件應(yīng)用根據(jù)不同的環(huán)境會(huì)有不同的行為邏輯,例如上面講到的 appsettings.{environment}.json 根據(jù)環(huán)境而不同的配置文件,例如之前的 入口文件 文章中講到的 Startup 文件根據(jù)不同環(huán)境的分離配置方式,而我們?cè)诖a中有時(shí)也會(huì)根據(jù)環(huán)境處理不同的邏輯,這時(shí)候我們可以注入 IHostEnvironment 服務(wù),通過它獲取當(dāng)前應(yīng)用的運(yùn)行環(huán)境,入口文件中無論是 WebApplicationBuilder 對(duì)象還是 WebApplication 對(duì)象都包含該類型的屬性。

通過環(huán)境變量設(shè)置當(dāng)前運(yùn)行環(huán)境,其實(shí)環(huán)境變量的值只是一個(gè)字符串,我們可以設(shè)置成任意值,這是運(yùn)行的,.NET Core 框架下 IHostEnvironment 也能夠正常加載到相應(yīng)的環(huán)境名稱,但是 .NET Core 默認(rèn)只提供了對(duì) Development、ProductionStaging 三種環(huán)境的判別,以及相應(yīng)的處理邏輯和擴(kuò)展方法,如果是其他的自定義環(huán)境則需要開發(fā)人員自行進(jìn)行相應(yīng)的處理了。和 .NET Core 應(yīng)用環(huán)境相關(guān)的知識(shí)點(diǎn)大家可以看一下官方文檔: 在 ASP.NET Core 中使用多個(gè)環(huán)境 | Microsoft Learn

除了上面的,其他還有一些主機(jī)配置,例如 URLS,但這個(gè)是可以通過應(yīng)用配置設(shè)置的,讀取相應(yīng)的配置值時(shí)也應(yīng)用從應(yīng)用配置讀取。

URLS 配置Web應(yīng)用啟動(dòng)后的訪問地址,這個(gè)配置可以多個(gè)地方設(shè)置,其中命令行參數(shù)最優(yōu)先,其他地方設(shè)置的應(yīng)該被命令行參數(shù)覆蓋。但是如果通過 Kestrel 終結(jié)點(diǎn)方式設(shè)置了 Web 應(yīng)用的訪問地址,那 Kestrel 終結(jié)點(diǎn)的配置將覆蓋其他所有的訪問地址的配置。

如在 appsettings.json 中添加以下配置:

"Kestrel": {
  "Endpoints": {
    "Https": {
      "Url": "https://localhost:9999"
    }
  }
}

那么以下幾種方式設(shè)置的 URLS 都會(huì)失效:

  • UseUrls
  • 命令行上的 --urls
  • 環(huán)境變量 ASPNETCORE__URLS

也就是說,就算我們用以下命令啟動(dòng)應(yīng)用,應(yīng)用最終的訪問地址還是以 Kestrel 終結(jié)點(diǎn)配置的為準(zhǔn):

dotnet run --urls="https://localhost:7777"

Kestrel 配置與 URLS 配置不是一個(gè)參數(shù),我們可以通過在命令行或者環(huán)境變量中設(shè)置 kestrel 中間點(diǎn)配置來覆蓋 appsettings.json 中的,這又回到配置提供程序的優(yōu)先級(jí)問題了。

set Kestrel__Endpoints__Https__Url=https://localhost:8888
	
dotnet run Kestrel__Endpoints__Https__Url=https://localhost:8888

在主機(jī)啟動(dòng)的邏輯中 Kestrel 具備更高的最終優(yōu)先級(jí),但是其實(shí)主機(jī)內(nèi)部是先根據(jù) URLS 創(chuàng)建了一個(gè)終結(jié)點(diǎn),之后又替換為 Kestrel 配置的終結(jié)點(diǎn)的。通過應(yīng)用啟動(dòng)時(shí)的控制臺(tái)輸出可以看出。
ASP.NET Core —配置系統(tǒng)
這種情況對(duì)于單機(jī)應(yīng)用沒有什么影響,但是對(duì)于使用自動(dòng)服務(wù)發(fā)現(xiàn)的微服務(wù)架構(gòu)而言就可能有問題了,可能導(dǎo)致注冊(cè)服務(wù)注冊(cè)中心的終結(jié)點(diǎn)是第一個(gè),而后應(yīng)用終結(jié)點(diǎn)又被改變,導(dǎo)致注冊(cè)中心記錄的服務(wù)終結(jié)點(diǎn)和實(shí)際的不一致。

4. 自定義配置提供程序

在 .NET Core 配置系統(tǒng)中封裝一個(gè)配置提供程序關(guān)鍵在于提供相應(yīng)的 IconfigurationSource 實(shí)現(xiàn)和 IConfigurationProvider 接口實(shí)現(xiàn),這兩個(gè)接口在上面也有提到了。

IConfigurationSource

IConfigurationSource 負(fù)責(zé)創(chuàng)建 IConfigurationProvider 實(shí)現(xiàn)的實(shí)例。它的定義很簡(jiǎn)單,就一個(gè)Build方法,返回 IConfigurationProvider 實(shí)例:

public interface IConfigurationSource
{
    IConfigurationProvider Build(IConfigurationBuilder builder);
}

IConfigurationProvider

IConfigurationProvider 負(fù)責(zé)實(shí)現(xiàn)配置的設(shè)置、讀取、重載等功能,并以鍵值對(duì)形式提供配置。

public interface IConfigurationProvider
{
    // 獲取指定父路徑下的直接子節(jié)點(diǎn)Key,然后 Concat(earlierKeys) 一同返回
    IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);
    
    // 當(dāng)該配置提供程序支持更改追蹤(change tracking)時(shí),會(huì)返回 change token
    // 否則,返回 null
    IChangeToken GetReloadToken();

    // 加載配置
    void Load();

    // 設(shè)置 key:value
    void Set(string key, string value);

    // 嘗試獲取指定 key 的 value
    bool TryGet(string key, out string value);
}

像工作中常用的配置中心客戶端,例如 nacos、consul,都是實(shí)現(xiàn)了對(duì)應(yīng)的配置提供程序,從而將配置中心中的配置無縫地接入到 .NET Core 的配置系統(tǒng)中進(jìn)行使用,和本地配置文件的使用沒有分別。

如果我們需要封裝自己的配置提供程序,推薦直接繼承抽象類 ConfigurationProvider,該類實(shí)現(xiàn)了 IConfigurationProvider 接口,繼承自該類只要實(shí)現(xiàn) Load 方法即可,Load 方法用于從配置來源加載解析配置信息,將最終的鍵值對(duì)配置信息存儲(chǔ)到 Data 中。這個(gè)過程中可參考一下其他已有的配置提供程序的源碼,模仿著去寫自己的東西。

在我們?nèi)粘5南到y(tǒng)平臺(tái)中,總少不了數(shù)據(jù)字典這樣一個(gè)功能,用于維護(hù)平臺(tái)中一些業(yè)務(wù)配置,因?yàn)槭请S業(yè)務(wù)動(dòng)態(tài)擴(kuò)展和變動(dòng)的,很多時(shí)候不會(huì)寫在配置文件,而是維護(hù)在數(shù)據(jù)庫中。以下以這樣一個(gè)場(chǎng)景實(shí)現(xiàn)一個(gè)配置提供程序。

因?yàn)槭且詳?shù)據(jù)庫作為載體來存儲(chǔ)配置信息,所以第一步就是定義實(shí)體類

public class DataDictioaryDO
{
    public int Id { get; set; }

    public int? ParentId { get; set; }

    public string Key { get; set; }

    public string Value { get; set; }
}

數(shù)據(jù)字典支持多級(jí)級(jí)聯(lián),通過 ParentId 關(guān)聯(lián)上一級(jí),ParentId 為空的即為根節(jié)點(diǎn),如存在下級(jí)節(jié)點(diǎn)則 Value 值可以為空,就算填寫了也無效,最終呈現(xiàn)出來的就是一個(gè)樹結(jié)構(gòu)。

然后就是定義相應(yīng)的數(shù)據(jù)庫訪問上下問 DataDictionaryDbContext

public class DataDictionaryDbContext : DbContext
{
	  public DbSet<DataDictioaryDO> DataDictioaries { get; set; }
	
	  public DataDictionaryDbContext(DbContextOptions<DataDictionaryDbContext> options) : base(options)
	  {
	  }
	  
	  protected override void OnModelCreating(ModelBuilder modelBuilder)
	  {
	      base.OnModelCreating(modelBuilder);
	      modelBuilder.Entity<DataDictioaryDO>().HasKey(e => e.Id);
	      modelBuilder.Entity<DataDictioaryDO>().Property(e => e.Value).IsRequired(false);
	  }
}

通過 DbContextOptions 交由外部去配置具體的數(shù)據(jù)庫類型和連接字符串。

之后創(chuàng)建 IConfigurationSource 實(shí)現(xiàn)類,主要就是構(gòu)造函數(shù)中需要傳入數(shù)據(jù)庫配置委托,并且在 Build 實(shí)例化EFDataDictionaryConfigurationProvider 對(duì)象。

public class EFDataDictionaryConfigurationSource : IConfigurationSource
{
    private readonly Action<DbContextOptionsBuilder> _action;

    public EFDataDictionaryConfigurationSource(Action<DbContextOptionsBuilder> action)
    {
        _action= action;
    }

    public IConfigurationProvider Build(IConfigurationBuilder builder)
    {
        return new EFDataDictionaryConfigurationProvider(_action);
    }
}

之后通過繼承 ConfigurationProvider 實(shí)現(xiàn) EFDataDictionaryConfigurationProvider,主要邏輯就是從數(shù)據(jù)庫獲取對(duì)應(yīng)的數(shù)據(jù)表,如果表中沒有數(shù)據(jù)則插入默認(rèn)數(shù)據(jù),再通過相應(yīng)的解析器解析數(shù)據(jù)表數(shù)據(jù)生成一個(gè) Dictionary<string, string> 對(duì)象。

public class EFDataDictionaryConfigurationProvider : ConfigurationProvider
{
    Action<DbContextOptionsBuilder> OptionsAction { get; }

    public EFDataDictionaryConfigurationProvider(Action<DbContextOptionsBuilder> action)
    {
        OptionsAction = action;
    }

    public override void Load()
    {
        var builder = new DbContextOptionsBuilder<DataDictionaryDbContext>();

        OptionsAction(builder);

        using var dbContext = new DataDictionaryDbContext(builder.Options);
        if(dbContext == null)
        {
            throw new Exception("Null DB Context !");
        }

        dbContext.Database.EnsureCreated();

        if (!dbContext.DataDictioaries.Any())
        {
            CreateAndSaveDefaultValues(dbContext);
        }

        Data = EFDataDictionaryParser.Parse(dbContext.DataDictioaries);
    }

    private void CreateAndSaveDefaultValues(DataDictionaryDbContext context)
    {
        var datas = new List<DataDictioaryDO>
        {
            new DataDictioaryDO
            {
                Id = 1,
                Key = "Settings",
            },
            new DataDictioaryDO
            {
                Id = 2,
                ParentId = 1,
                Key = "Provider",
                Value = nameof(EFDataDictionaryConfigurationProvider)
            },
            new DataDictioaryDO
            { 
                Id = 3,
                ParentId = 1,
                Key = "Version",
                Value = "v1.0.0"
            }
        };

        context.DataDictioaries.AddRange(datas);
        context.SaveChanges();
    }
}

其中,解析器 EFDataDictionaryParser 的代碼如下,主要就是通過遞歸的方式,通過樹形數(shù)據(jù)的 key 構(gòu)建構(gòu)建完整的 key,并將其存入 Dictionary<string,string> 對(duì)象中。

internal class EFDataDictionaryParser
{
    private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    private readonly Stack<string> _context = new();
    private string _currentPath;

    private EFDataDictionaryParser() { }

    public static IDictionary<string, string> Parse(IEnumerable<DataDictioaryDO> datas) =>
        new EFDataDictionaryParser().ParseDataDictionaryConfiguration(datas);

    private IDictionary<string, string> ParseDataDictionaryConfiguration(IEnumerable<DataDictioaryDO> datas)
    {
        _data.Clear();

        if(datas?.Any() != true)
        {
            return _data;
        }

        var roots = datas.Where(d => !d.ParentId.HasValue);
        foreach (var root in roots)
        {
            EnterContext(root.Key);
            VisitElement(datas, root);
            ExitContext();
        }
        return _data;
    }

    private void VisitElement(IEnumerable<DataDictioaryDO> datas, DataDictioaryDO parent)
    {
        var children = datas.Where(d => d.ParentId == parent.Id);
        if (children.Any())
        {
            foreach (var section in children)
            {
                EnterContext(section.Key);
                VisitElement(datas, section);
                ExitContext();
            }
        }
        else
        {
            var key = _currentPath;
            if (_data.ContainsKey(key))
                throw new FormatException($"A duplicate key '{key}' was found.");

            _data[key] = parent.Value;
        }
    }

    private void EnterContext(string context)
    {
        _context.Push(context);
        _currentPath = ConfigurationPath.Combine(_context.Reverse());
    }

    private void ExitContext()
    {
        _context.Pop();
        _currentPath = ConfigurationPath.Combine(_context.Reverse());
    }
}

之后為這個(gè)配置提供程序提供一個(gè)擴(kuò)展方法,方便之后的使用,如下:

public static class EFDataDictionaryConfigurationExtensions
{
    public static IConfigurationBuilder AddEFDataDictionaryConfiguration(this IConfigurationBuilder builder, 
        Action<DbContextOptionsBuilder> optionAction)
    {
        builder.Add(new EFDataDictionaryConfigurationSource(optionAction));
        return builder;
    }
}

之后在入口文件中將我們的配置擴(kuò)展程序添加到配置系統(tǒng)中,并指定使用內(nèi)存數(shù)據(jù)庫進(jìn)行測(cè)試

using ConfigurationSampleConsole.ConfigProvider;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((context, config) =>
    {
        // 清除原有的配置提供程序
        config.Sources.Clear();

        config.AddEFDataDictionaryConfiguration(builder =>
        {
            builder.UseInMemoryDatabase("DataDictionary");
        });
    })
    .Build();

var configuration = host.Services.GetService<IConfiguration>();

Console.WriteLine($"Settings:Provider: {configuration.GetValue<string>("Settings:Provider")}");
Console.WriteLine($"Settings:Version: {configuration.GetValue<string>("Settings:version")}");

host.Run();

最后的控制臺(tái)輸出結(jié)果如下:

ASP.NET Core —配置系統(tǒng)
以上就是 .NET Core 框架下配置系統(tǒng)的一部分知識(shí)點(diǎn),更加詳盡的介紹大家可以再看看官方文檔。配置系統(tǒng)很多時(shí)候是結(jié)合選項(xiàng)系統(tǒng)儀器一起使用的,下一篇將介紹一下 .NET Core 框架下的選項(xiàng)系統(tǒng)。

?
?

參考文章:

ASP.NET Core 中的配置 | Microsoft Learn
配置 - .NET | Microsoft Learn
理解ASP.NET Core - 配置(Configuration) - xiaoxiaotank

?
?

ASP.NET Core 系列總結(jié):
上一篇:ASP.NET Core — 依賴注入
下一篇:ASP.NET Core — 選項(xiàng)系統(tǒng)文章來源地址http://www.zghlxwxcb.cn/news/detail-478223.html

到了這里,關(guān)于ASP.NET Core —配置系統(tǒng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【ASP.NET Core】MVC過濾器:運(yùn)行流程

    【ASP.NET Core】MVC過濾器:運(yùn)行流程

    MVC 的過濾器(Filters)也翻譯為“篩選器”。但是老周更喜歡翻譯為“過濾器”,意思上更好理解。 既然都叫過濾器了,就是在MVC的操作方法調(diào)用前后進(jìn)行特殊處理的類型。比如: a、此調(diào)用是否已授權(quán)? b、在模型綁定之前要不要修改數(shù)據(jù)源?(可能含有兒童不宜的數(shù)據(jù))

    2024年02月05日
    瀏覽(21)
  • ASP.NET Core MVC -- 將視圖添加到 ASP.NET Core MVC 應(yīng)用

    ASP.NET Core MVC -- 將視圖添加到 ASP.NET Core MVC 應(yīng)用

    右鍵單擊“視圖”文件夾,然后單擊“添加”“新文件夾”,并將文件夾命名為“HelloWorld”。 右鍵單擊“Views/HelloWorld”文件夾,然后單擊“添加”“新項(xiàng)”。 在“添加新項(xiàng) - MvcMovie”對(duì)話框中: 在右上角的搜索框中,輸入“視圖” 選擇“Razor 視圖 - 空” 保持“名稱”框的

    2024年02月13日
    瀏覽(127)
  • asp.net core之實(shí)時(shí)應(yīng)用

    asp.net core之實(shí)時(shí)應(yīng)用

    本文將介紹ASP.NET Core SignalR,這是一個(gè)強(qiáng)大的實(shí)時(shí)通信庫,用于構(gòu)建實(shí)時(shí)、雙向通信應(yīng)用程序。我們將探討SignalR的基本概念、架構(gòu)和工作原理,并提供一些示例代碼來幫助讀者更好地理解和使用SignalR。 ASP.NET Core SignalR提供了一種簡(jiǎn)單而強(qiáng)大的方式來構(gòu)建實(shí)時(shí)通信應(yīng)用程序。

    2024年02月14日
    瀏覽(22)
  • 【ASP.NET Core 基礎(chǔ)知識(shí)】--最佳實(shí)踐和進(jìn)階主題--設(shè)計(jì)模式在ASP.NET Core中的應(yīng)用

    一、設(shè)計(jì)模式概述 1.1 什么是設(shè)計(jì)模式 設(shè)計(jì)模式是在軟件設(shè)計(jì)過程中反復(fù)出現(xiàn)的、經(jīng)過驗(yàn)證的、可重用的解決問題的方法。它們是針對(duì)特定問題的通用解決方案,提供了一種在軟件開發(fā)中可靠的指導(dǎo)和標(biāo)準(zhǔn)化方法。設(shè)計(jì)模式通常描述了一種在特定情景下的解決方案,包括了問

    2024年02月21日
    瀏覽(850)
  • ASP.NET Core教程:ASP.NET Core 程序部署到Windows系統(tǒng)

    ASP.NET Core教程:ASP.NET Core 程序部署到Windows系統(tǒng)

    本篇文章介紹如何將一個(gè)ASP.NET Core Web程序部署到Windows系統(tǒng)上。這里以ASP.NET Core WebApi為例進(jìn)行講解。首先創(chuàng)建一個(gè)ASP.NET Core WebApi項(xiàng)目,使用默認(rèn)的Values控制器,這里使用Visual Studio 2019創(chuàng)建一個(gè)ASP.NET Core 3.1d的WebApi項(xiàng)目。 創(chuàng)建新項(xiàng)目的時(shí)候選項(xiàng)ASP.NET Core Web應(yīng)用程序,如下圖所

    2023年04月08日
    瀏覽(103)
  • ASP.NET Core 配置系列一

    ASP.NET Core 配置系列一

    A S P . N E T ? C o r e ? 配 置 主 要 通 過 這 3 個(gè) 文 件 設(shè) 置 : 1 ? 項(xiàng) 目 文 件 也 叫 . c s p r o j ? 文 件 2 ? P r o g r a m . c s 3 ? a p p s e t t i n g s . j s o n 這 些 配 置 告 訴 A S P . N E T ? C o r e ? 應(yīng) 用 程 序 基 于 用 戶 的 交 互 是 如 何 工 作 的, 在 本 節(jié) 中 我 們 理 解 A S P .

    2024年02月03日
    瀏覽(99)
  • ASP.NET Core實(shí)時(shí)庫SignalR簡(jiǎn)單應(yīng)用

    ASP.NET Core實(shí)時(shí)庫SignalR簡(jiǎn)單應(yīng)用

    SignalR 是用于構(gòu)建需要實(shí)時(shí)用戶交互或?qū)崟r(shí)數(shù)據(jù)更新的Web 應(yīng)用程序的一個(gè)開放源代碼.NET 庫。不僅僅用在Web應(yīng)用中,后面會(huì)講到它的應(yīng)用范圍。它簡(jiǎn)化了簡(jiǎn)化了構(gòu)建實(shí)時(shí)應(yīng)用程序的過程,包括 ASP.NET Server 庫和 JavaScript Client 庫,以便管理Client與Server連接并將內(nèi)容更新推送給Cl

    2024年02月11日
    瀏覽(23)
  • ASP.NET Core如何知道一個(gè)請(qǐng)求執(zhí)行了哪些中間件?

    ASP.NET Core如何知道一個(gè)請(qǐng)求執(zhí)行了哪些中間件?

    需要添加兩個(gè)Nuget包分別是: Microsoft.AspNetCore.MiddlewareAnalysis 和 Microsoft.Extensions.DiagnosticAdapter ,前者是分析記錄中間件核心代碼實(shí)現(xiàn)后者是用來接收日志輸出的,由于是用的DiagnosticSource方式記錄日志,所以需要使用DiagnosticListener對(duì)象的SubscribeWithAdapter方法來訂閱。 這個(gè)適配器

    2023年04月09日
    瀏覽(100)
  • 一個(gè)基于ASP.NET Core完全開源的CMS 解決方案

    一個(gè)基于ASP.NET Core完全開源的CMS 解決方案

    MixCore CMS是一個(gè)基于.NET Core框架的開源內(nèi)容管理系統(tǒng)(CMS),提供了豐富的的基礎(chǔ)功能和插件,是一款面向未來的企業(yè) Web? CMS ,可輕松構(gòu)建任何類型的應(yīng)用程序。集成了Google Analytics分析,以及友好的Seo功能,非常適合用于創(chuàng)建企業(yè)網(wǎng)站、內(nèi)容系統(tǒng)、個(gè)人博客,也可以用于開發(fā)

    2024年02月05日
    瀏覽(113)
  • ASP.NET Core Web應(yīng)用程序項(xiàng)目部署流程

    ASP.NET Core Web應(yīng)用程序項(xiàng)目部署流程

    目錄 一、準(zhǔn)備ASP.NET Core應(yīng)用程序部署文件 二、環(huán)境配置 三、測(cè)試 ASP.NET Core Web 應(yīng)用程序 四、部署后訪問失敗問題 以下部署流程都是基于Windows服務(wù)器環(huán)境下進(jìn)行的。 一、準(zhǔn)備ASP.NET Core應(yīng)用程序部署文件 使用 Visual Studio 開發(fā)工具創(chuàng)建 ASP.NET Core 的Web應(yīng)用程序,利用VS工具發(fā)布

    2024年02月05日
    瀏覽(36)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包