日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

ASP.NET Core的配置(2):配置模型详解

發(fā)布時(shí)間:2025/3/13 asp.net 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET Core的配置(2):配置模型详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在上面一章我們以實(shí)例演示的方式介紹了幾種讀取配置的幾種方式,其中涉及到三個(gè)重要的對(duì)象,它們分別是承載結(jié)構(gòu)化配置信息的Configuration,提供原始配置源數(shù)據(jù)的ConfigurationProvider,以及作為“中間人”的ConfigurationBuilder。接下來我們將會(huì)對(duì)由這三個(gè)核心對(duì)象組成的配置模型進(jìn)行詳細(xì)介紹,不過在此之前我們有必要來認(rèn)識(shí)配置信息在不同載體中所體現(xiàn)出來的三種結(jié)構(gòu)。

目錄
一、配置的三種結(jié)構(gòu)
邏輯結(jié)構(gòu)
原始結(jié)構(gòu)
物理結(jié)構(gòu)
結(jié)構(gòu)轉(zhuǎn)換
二、Configuration
三、ConfigurationProvider
四、ConfigurationBuilder

一、配置的三種結(jié)構(gòu)

相同的數(shù)據(jù)具有不同的表現(xiàn)和承載方式,同時(shí)體現(xiàn)出不同的數(shù)據(jù)結(jié)構(gòu)。對(duì)于配置來說,它在消費(fèi)過程中是以Configuration對(duì)象的形式來體現(xiàn)的,該對(duì)象邏輯上具有一個(gè)樹形化的層次結(jié)構(gòu)。配置具有多種來源,可以是內(nèi)存對(duì)象、物理文件或者數(shù)據(jù)庫,不同類型的數(shù)據(jù)源決定了不同的配置結(jié)構(gòu)。我們將這兩種結(jié)構(gòu)稱為邏輯結(jié)構(gòu)和原始結(jié)構(gòu)。在這兩種結(jié)構(gòu)之間,配置還存在一種中間結(jié)構(gòu),我們姑且稱之為物理結(jié)構(gòu)。

邏輯結(jié)構(gòu)

配置的邏輯結(jié)構(gòu)就是Configuration對(duì)象所體現(xiàn)的結(jié)構(gòu),說得更加準(zhǔn)確一點(diǎn)應(yīng)該是針對(duì)Configuration對(duì)象的API所體現(xiàn)的結(jié)構(gòu)(因?yàn)椴皇撬械腃onfiguration對(duì)象內(nèi)部都封裝一組配置數(shù)據(jù))。配置在邏輯上呈現(xiàn)為一種樹形結(jié)構(gòu),我們稱之為配置樹,組成這棵樹的某個(gè)節(jié)點(diǎn)就體現(xiàn)為一個(gè)Configuration對(duì)象。表現(xiàn)為鍵值對(duì)的原子配置項(xiàng)存儲(chǔ)于葉子節(jié)點(diǎn)中,而非葉子節(jié)點(diǎn)僅僅體現(xiàn)為一個(gè)配置節(jié)點(diǎn)的邏輯容器,自身不包含具體的配置數(shù)據(jù)。對(duì)于我們?cè)诘谝还?jié)定義的FormatSettings來說,它對(duì)應(yīng)的配置具有如右圖所示的邏輯結(jié)構(gòu)。

原始結(jié)構(gòu)

配置采用怎樣的原始結(jié)構(gòu)取決于我們采用何種方式定義它。最常見的配置源體現(xiàn)為采用某個(gè)格式的文本文件,那么配置的原始結(jié)構(gòu)則由文件的格式來決定。對(duì)于我們?cè)诘谝还?jié)定義的FormatSettings類型,我們可以按照如下的形式以XML和JSON的格式來定義其配置。

XML:

1: <Format> 2: <DateTime> 3: <LongDatePattern>dddd, MMMM d, yyyy</LongDatePattern> 4: <LongTimePattern>h:mm:ss tt</LongTimePattern> 5: <ShortDatePattern>M/d/yyyy</ShortDatePattern> 6: <ShortTimePattern>h:mm tt</ShortTimePattern> 7: </DateTime> 8: <CurrencyDecimal> 9: <Digits>2</Digits> 10: <Symbol>$</Symbol> 11: </CurrencyDecimal> 12: </Format>

JSON:

1: { 2: "format": { 3: "dateTime": { 4: "longDatePattern" : "dddd, MMMM d, yyyy", 5: "longTimePattern" : "h:mm:ss tt", 6: "shortDatePattern" : "M/d/yyyy", 7: "shortTimePattern" : "h:mm tt" 8: }, 9: "currencyDecimal": { 10: "digits": "2", 11: "symbol": "$" 12: } 13: } 14: }

物理結(jié)構(gòu)

配置模型的終極目的就是將配置從原始結(jié)構(gòu)轉(zhuǎn)換成邏輯結(jié)構(gòu)。不過在進(jìn)行結(jié)構(gòu)轉(zhuǎn)化的時(shí)候,它并不會(huì)直接將原始的配置數(shù)據(jù)轉(zhuǎn)換成一個(gè)Configuration對(duì)象,它們之間由一種被我稱為物理結(jié)構(gòu)的中間結(jié)構(gòu)作為過度。配置的物理結(jié)構(gòu)體現(xiàn)為一個(gè)簡(jiǎn)單的數(shù)據(jù)字典。同樣是針對(duì)FormatSettings這個(gè)類型,對(duì)應(yīng)的配置將具有如下表所示的物理結(jié)構(gòu)。

結(jié)構(gòu)轉(zhuǎn)換

配置模型的終極目的在于將具有不同來源的配置轉(zhuǎn)換成Configuration對(duì)象,配置源和Configuration對(duì)象本身分別體現(xiàn)了配置的原始結(jié)構(gòu)和邏輯結(jié)構(gòu),所以配置模型旨在實(shí)現(xiàn)配置數(shù)據(jù)從原始結(jié)構(gòu)向邏輯結(jié)構(gòu)的轉(zhuǎn)換。在具體轉(zhuǎn)換過程中,配置模型先利用與配置源相對(duì)應(yīng)的ConfigurationProvider將配置數(shù)據(jù)從原始結(jié)構(gòu)轉(zhuǎn)換成體現(xiàn)為數(shù)據(jù)字典的物理結(jié)構(gòu)。當(dāng)我們利用ConfigurationBuilder生成Configuration的時(shí)候,實(shí)際上將配置數(shù)據(jù)從物理結(jié)構(gòu)轉(zhuǎn)換成邏輯結(jié)構(gòu)。

二、Configuration

我們?cè)谏厦嬉詳?shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換的角度分析了Configuratin、ConfigurationProvider和ConfigurationBuilder這三個(gè)核心對(duì)象在配置模型中所起的作用,接下來讓我們來更加深入地認(rèn)識(shí)它們。我們首先來介紹Configuration對(duì)象,本章不斷提及的Configuration泛指類型實(shí)現(xiàn)了IConfiguration接口的對(duì)象,該接口定義在“Microsoft.Extensions.Configuration”命名空間下,如果未作特別說明,本章涉及到的與配置相關(guān)的類型均定義在此命名空間下。

1: public interface IConfiguration 2: { 3: IEnumerable<IConfigurationSection> GetChildren(); 4: IConfigurationSection GetSection(string key); 5: IChangeToken GetReloadToken(); 6: 7: string this[string key] { get; set; } 8: }

配置具有樹形邏輯結(jié)構(gòu),一個(gè)Configuration對(duì)象表示配置樹的某個(gè)配置節(jié)點(diǎn)。對(duì)于組成整棵樹的所有配置節(jié)點(diǎn)來說,表示根節(jié)點(diǎn)的Configuration對(duì)象與表示其它配置節(jié)點(diǎn)的Configuration對(duì)象相比具有不同的特性,所以配置模型采用不同的接口來表示它們。具體來說,基于根節(jié)點(diǎn)的Configuration對(duì)象通過接口IConfigurationRoot表示,另一個(gè)接口IConfigurationSection則表示針對(duì)非空節(jié)點(diǎn)的Configuration對(duì)象,兩個(gè)接口都繼承IConfiguration。如右圖所示,一棵完整的配置樹由一個(gè)ConfigurationRoot對(duì)象和若干ConfigurationSection構(gòu)成。

ConfigurationRoot

我們將所有實(shí)現(xiàn)了IConfigurationRoot接口的類型和其對(duì)象統(tǒng)稱為ConfigurationRoot。如下面的代碼片段所示,IConfigurationRoot僅僅包含一個(gè)唯一的方法Reload實(shí)現(xiàn)對(duì)配置數(shù)據(jù)的重新加載。一個(gè)ConfigurationRoot對(duì)象表示配置數(shù)的根節(jié)點(diǎn),如果它被重新加載了,那么這顆配置樹承載的所有配置數(shù)據(jù)均被重新加載了。

1: public interface IConfigurationRoot : IConfiguration 2: { 3: void Reload(); 4: }

ConfigurationSection

我們將所有實(shí)現(xiàn)了IConfigurationSection接口的類型及其對(duì)象統(tǒng)稱為ConfigurationSection,一個(gè)ConfigurationSection對(duì)應(yīng)著配置樹中某個(gè)非根配置節(jié)。IConfigurationSection具有如下三個(gè)屬性,只讀屬性Key用來唯一標(biāo)識(shí)多個(gè)“同父”配置節(jié),而另一個(gè)只讀屬性Path則表示從根節(jié)點(diǎn)到父節(jié)點(diǎn)的路徑,該路徑由ConfigurationSection的Key組成,并采用冒號(hào)作為分隔符。Path和Key的組合體現(xiàn)了當(dāng)前配置節(jié)在整個(gè)配置樹中的位置。

1: public interface IConfigurationSection : IConfiguration 2: { 3: string Path { get; } 4: string Key { get; } 5: string Value { get; set; } 6: }

IConfigurationSection的Value屬性表示配置節(jié)的值,在大部分情況下,只有配置樹葉子結(jié)點(diǎn)對(duì)應(yīng)的ConfigurationSection對(duì)象才具有值,非葉子節(jié)點(diǎn)對(duì)應(yīng)的ConfigurationSection對(duì)象實(shí)際上僅僅表示一組隸屬于它的所有子配置節(jié)的邏輯容器,它們的Value一般返回Null。值得一體的是,這個(gè)Value屬性并不是只讀的,而是可讀可寫的。

在對(duì)ConfigurationRoot和ConfigurationSection具有基本了解情況下我們回過頭來看看定義在接口IConfiguration中的成員。它的GetChildren方法返回一組表示其子配置節(jié)的ConfigurationSection對(duì)象集合,另一個(gè)方法GetSection則根據(jù)指定的Key返回對(duì)應(yīng)的ConfigurationSection對(duì)象。當(dāng)GetSection方法執(zhí)行的時(shí)候,指定的參數(shù)將會(huì)與當(dāng)前ConfigurationSection的Path進(jìn)行組合以確定目標(biāo)ConfigurationSection所在的路徑,所以如果在調(diào)用該方法的時(shí)候指定一個(gè)相對(duì)于當(dāng)前配置節(jié)的路徑,我們是可以得到子節(jié)點(diǎn)以下的某個(gè)配置節(jié)。

1: Dictionary<string, string> source = new Dictionary<string, string> 2: { 3: ["A:B:C"] = "ABC" 4: }; 5: IConfiguration root = new ConfigurationBuilder() 6: .Add(new MemoryConfigurationProvider(source)) 7: .Build(); 8:? 9: IConfigurationSection section1 = root.GetSection("A:B:C"); 10: IConfigurationSection section2 = root.GetSection("A:B").GetSection("C"); 11: IConfigurationSection section3 = root.GetSection("A").GetSection("B:C"); 12:? 13: Debug.Assert(section1.Value == section2.Value && section2.Value == section3.Value); 14: Debug.Assert(!ReferenceEquals(section1, section2) && !ReferenceEquals(section2, section3)); 15: Debug.Assert(null == root.GetSection(Guid.NewGuid().ToString()));

如上面的代碼片段所示,我們以不同的方式調(diào)用GetSection方法得到的都是路徑為“Format:DateTime:LongDatePattern”的ConfigurationSection。上面這段代碼還體現(xiàn)了另一個(gè)有趣的現(xiàn)象,雖然這三個(gè)ConfigurationSection對(duì)象均指向配置樹的同一個(gè)節(jié)點(diǎn),但是它們卻并非同一個(gè)對(duì)象。換句話說,當(dāng)我們調(diào)用GetSection方法的時(shí)候,不論配置樹種是否存在一個(gè)與指定路徑匹配的配置節(jié),它總是會(huì)創(chuàng)建一個(gè)全新的ConfigurationSection對(duì)象。

IConfiguration還具有一個(gè)索引,我們可以指定子配置節(jié)的Key或者相對(duì)當(dāng)前配置節(jié)的路徑得到對(duì)應(yīng)配置節(jié)的值。當(dāng)這個(gè)索引執(zhí)行的時(shí)候,它會(huì)按照與GetSection方法完全一致的邏輯得到一個(gè)ConfigurationSection對(duì)象,并返回其Value屬性。如果配置樹中不具有匹配的配置節(jié),該索引會(huì)返回Null而不會(huì)拋出異常。

三、ConfigurationProvider

為配置模型提供原始配置數(shù)據(jù)的ConfigurationProvider是對(duì)所有實(shí)現(xiàn)了IConfigurationProvider接口的所有類型及其對(duì)象的統(tǒng)稱。從配置數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換的角度來看,ConfigurationProvider的目的在于將配置數(shù)據(jù)從原始結(jié)構(gòu)轉(zhuǎn)換成物理結(jié)構(gòu),由于配置數(shù)據(jù)的物理結(jié)構(gòu)體現(xiàn)為一個(gè)簡(jiǎn)單的二維數(shù)據(jù)字典,所以我們會(huì)發(fā)現(xiàn)定義在IConfigurationProvider接口中的方法大都體現(xiàn)為針對(duì)字典對(duì)象的相關(guān)操作。

1: public interface IConfigurationProvider 2: { 3: void Load(); 4:? 5: bool TryGet(string key, out string value); 6: void Set(string key, string value); 7: IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath, string delimiter) 8: }

配置數(shù)據(jù)的加載通過調(diào)用ConfigurationProvider的Load方法來完成。我們可以調(diào)用TryGet方法獲取有指定的Key所標(biāo)識(shí)的配置項(xiàng)的值。從數(shù)據(jù)持久化的角度來講,ConfigurationProvider基本上都是只讀的,也就是說ConfigurationProvider只負(fù)責(zé)從持久化資源中讀取配置數(shù)據(jù),而不負(fù)責(zé)更新保存在持久化資源的配置數(shù)據(jù),所以它提供的Set方法設(shè)置的配置數(shù)據(jù)一般只會(huì)保存在內(nèi)存中。ConfigurationProvider的GetChildKeys方法用于獲取指定路徑對(duì)應(yīng)配置節(jié)的所有子節(jié)點(diǎn)的Key。

每種不同類型的配置源都具有對(duì)應(yīng)的ConfigurationProvider,它們對(duì)應(yīng)的類型大都不會(huì)直接實(shí)現(xiàn)IConfigurationProvider接口,而是繼承另一個(gè)名為ConfigurationProvider的抽象類。這個(gè)抽象類的定義其實(shí)很簡(jiǎn)單,從如下的代碼片段可以看出它僅僅是對(duì)一個(gè)IDictionary<string, string>對(duì)象的封裝,其Set和TryGetValue方法最終操作的都是這個(gè)字典對(duì)象。它實(shí)現(xiàn)了Load方法并將其定義成虛方法,具體的ConfigurationProvider可以通過重寫這個(gè)方法從相應(yīng)的數(shù)據(jù)源中讀取配置數(shù)據(jù)并對(duì)這個(gè)字典對(duì)象進(jìn)行初始化。

1: public abstract class ConfigurationProvider : IConfigurationProvider 2: { 3: protected IDictionary<string, string> Data { get; set; } 4:? 5: public IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath, string delimiter) 6: { 7: //省略實(shí)現(xiàn) 8: } 9:? 10: public virtual void Load() 11: {} 12:? 13: public void Set(string key, string value) 14: { 15: this.Data[key] = value; 16: } 17:? 18: public bool TryGet(string key, out string value) 19: { 20: return this.Data.TryGetValue(key, out value); 21: } 22: //其他成員 23: }

接下來我們簡(jiǎn)單介紹一下定義在這個(gè)抽象類中GetChildKeys方法的邏輯。采用基于路徑的Key讓數(shù)據(jù)字典在邏輯上具有了樹形化層次結(jié)構(gòu),而這個(gè)方法用于獲取將指定配置節(jié)作為父節(jié)點(diǎn)的所有配置節(jié)的Key。指定的父配置節(jié)通過參數(shù)parentPath表示的路徑來體現(xiàn),另一個(gè)參數(shù)delimiter則表示路徑采用的分隔符。除此之外,這個(gè)方法還具有一個(gè)字符串集合類型的參數(shù)earlierKeys,它表示預(yù)先解析出來的Key,這個(gè)列表會(huì)包含在返回的結(jié)果中。

1: class Program 2: { 3: static void Main(string[] args) 4: { 5: Dictionary<string, string> source = new Dictionary<string, string> 6: { 7: ["A:B:C"] = "", 8: ["A:B:D"] = "", 9: ["A:E"] = "", 10: }; 11:? 12: MemoryConfigurationProvider provider = new MemoryConfigurationProvider(source); 13: Console.WriteLine("{0, -20}{1}", "Parent Path", "Child Keys"); 14: Console.WriteLine("------------------------------------------"); 15:? 16: Print("Null", provider.GetChildKeys(new string[] { "X", "Y", "Z" }, null, ":")); 17: Print("A", provider.GetChildKeys(new string[] { "x", "y", "z" }, "A", ":")); 18: Print("A:B", provider.GetChildKeys(new string[] { "X", "Y", "Z }, "A:B", ":")); 19: Print("A:B:C",provider.GetChildKeys(new string[] { "X", "Y", "Z }, "A:B:C", ":")); 20: } 21:? 22: static void Print(string parentPath, IEnumerable<string> keys) 23: { 24: Console.WriteLine("{0, -20}{1}", parentPath, string.Join(", ", keys.ToArray())); 25: } 26: }

為了讓讀者朋友們能夠更加直觀地理解GetChildKeys方法的邏輯,我們編寫了如上一段實(shí)例程序。我們創(chuàng)建了一個(gè)MemoryConfigurationProvider對(duì)象,由塔封裝的配置數(shù)據(jù)字段包含三個(gè)元素,它們對(duì)應(yīng)的Key分別是“A:B:C”、“A:B:D”和“A:E”。我們調(diào)用它的GetChildKeys方法并將表示父節(jié)點(diǎn)的路徑分別指定為“A”、“A:B和“A:B:C”以獲取相應(yīng)子節(jié)點(diǎn)的Key。除此之外,我們采用冒號(hào)(“:”)作為分隔符,并將earlierKeys指定為包含“X”、“Y”和“Z”三個(gè)元素的數(shù)組。這段程序執(zhí)行之后會(huì)在控制臺(tái)上產(chǎn)生如下的輸出結(jié)果,我們從中可以看出一個(gè)細(xì)節(jié),返回的結(jié)構(gòu)并沒有將重復(fù)的Key剔除

1: Parent Path Child Keys 2: ------------------------------------------ 3: Null X, Y, Z 4: A B, B, E, X, Y, Z: 5: A:B C, D, X, Y, Z 6: A:B:C X, Y, Z


四、ConfigurationBuilder

ConfigurationBuilder泛指所有實(shí)現(xiàn)了IConfigurationBuilder接口的類型及其對(duì)象,它在配置模型中的作用就是利用注冊(cè)的ConfigurationProvider提取轉(zhuǎn)換成數(shù)據(jù)字典的配置數(shù)據(jù)并創(chuàng)建對(duì)應(yīng)的Configuration對(duì)象,具體來說創(chuàng)建的是一個(gè)體現(xiàn)配置樹的ConfigurationRoot對(duì)象。注冊(cè)到ConfigurationBuilder上的ConfigurationProvider體現(xiàn)為IConfigurationBuilder接口的Providers屬性,我們可以調(diào)用Add方法將ConfigurationProvider添加到這個(gè)集合中。

1: public interface IConfigurationBuilder 2: { 3: IEnumerable<IConfigurationProvider> Providers { get; } 4: Dictionary<string, object> Properties { get; } 5:? 6: IConfigurationBuilder Add(IConfigurationProvider provider); 7: IConfigurationRoot Build(); 8: }

除此之外,IConfigurationBuilder還具有一個(gè)字典類型的只讀屬性Properties,我們可以將任意自定義的屬性附加當(dāng)一個(gè)ConfigurationBuilder對(duì)象上,并通過對(duì)應(yīng)的Key得到這些屬性值。ConfigurationRoot的創(chuàng)建最終通過Build方法完成。

原生的配置模型中提供了一個(gè)實(shí)現(xiàn)IConfigurationBuilder接口的類型,那就是在我們之前演示的實(shí)例中多次使用的ConfigurationBuilder類,配置模型默認(rèn)的配置生成機(jī)制體現(xiàn)在它實(shí)現(xiàn)的Build方法中。具體來說,實(shí)現(xiàn)在ConfigurationBuilder類中的Build方法返回對(duì)象的真實(shí)類型為ConfigurationRoot,該對(duì)象通過一個(gè)類型為ConfigurationSection對(duì)象表示非根配置節(jié)。右圖所示的UML展示了配置模型中以Configuration、ConfigurationProvider和ConfigurationBuilder為核心的相關(guān)接口/類型以及它們之前的關(guān)系。

ConfigurationRoot和ConfigurationSection這個(gè)兩個(gè)類型的定義體現(xiàn)配置模型默認(rèn)采用怎樣的機(jī)制讀取配置數(shù)據(jù),這是我們本節(jié)論述的重點(diǎn)內(nèi)容。雖然配置模型最終提供的配置數(shù)據(jù)通過Configuration對(duì)象來體現(xiàn),但是不論ConfigurationRoot還是ConfigurationSection對(duì)象,它們自身本沒有封裝任何的形式的配置數(shù)據(jù)所有針對(duì)它們的數(shù)據(jù)讀寫操作最終都會(huì)轉(zhuǎn)移到相應(yīng)的ConfigurationProvider上。由于Configuration對(duì)象僅僅體現(xiàn)為ConfigurationProvider的代理,所以由同一個(gè)ConfigurationBuilder創(chuàng)建的所有ConfigurationRoot對(duì)象都是等效的,下面的代碼片段體現(xiàn)了這樣的等效性。

1: IConfigurationBuilder builder = new ConfigurationBuilder().Add(new MemoryConfigurationProvider()); 2:? 3: IConfiguration config1 = builder.Build(); 4: IConfiguration config2 = builder.Build(); 5:? 6: config1["Foobar"] = "ABC"; 7: Debug.Assert(config2["Foobar"] == "ABC");

組成配置樹的ConfigurationRoot和ConfigurationSection不但自身封裝和配置數(shù)據(jù),配置節(jié)父子關(guān)系的維護(hù)也并不直接通過對(duì)象之間的引用關(guān)系來維系。如右圖所示,對(duì)于一個(gè)表示配置樹中某個(gè)非根配置節(jié)的ConfigurationSection對(duì)象來說,它僅僅保留著對(duì)根節(jié)點(diǎn)的引用,后者是一個(gè)類型為ConfigurationRoot的對(duì)象。當(dāng)我們調(diào)用ConfigurationSection方法獲取或者設(shè)置配置數(shù)據(jù)的時(shí)候,它會(huì)直接將調(diào)用請(qǐng)求轉(zhuǎn)發(fā)給表示配置樹根的ConfigurationRoot對(duì)象。

具體來說,當(dāng)我們?cè)噲D通過某個(gè)ConfigurationSection對(duì)象得到對(duì)應(yīng)配置節(jié)點(diǎn)的值時(shí),該對(duì)象會(huì)將配置數(shù)據(jù)的讀取請(qǐng)求轉(zhuǎn)發(fā)給它所引用的表示數(shù)值樹根的ConfigurationRoot對(duì)象,同時(shí)將自身的路徑一并傳遞給后者。ConfigurationRoot最終利用ConfigurationProvider根據(jù)指定的路徑得到對(duì)應(yīng)配置項(xiàng)的值,左圖揭示了這樣的流程。

在對(duì)實(shí)現(xiàn)在ConfigurationRoot和ConfigurationSection這兩個(gè)類中針對(duì)配置的讀寫機(jī)制有了大概的了解之后,我們從代碼實(shí)現(xiàn)的角度來進(jìn)一步地來認(rèn)識(shí)這兩個(gè)類型,在這之前我們需要先來認(rèn)識(shí)一個(gè)名為ConfigurationPath的工具類。顧名思義,ConfigurationPath幫助我們實(shí)現(xiàn)針對(duì)配置樹路徑相關(guān)的計(jì)算,其中Combine方法將多個(gè)片段合并成一個(gè)完整的路徑,GetSectionKey方法會(huì)根據(jù)指定的路徑得到對(duì)應(yīng)的Key,而GetParentPath則根據(jù)指定的路徑得到上一級(jí)的路徑。

1: public static class ConfigurationPath 2: { 3: public static string Combine(params string[] pathSegements) ; 4: public static string Combine(IEnumerable<string> pathSegements) ; 5: public static string GetSectionKey(string path) ; 6: public static string GetParentPath(string path) ; 7: }

ConfigurationRoot

ConfigurationRoot真實(shí)的實(shí)現(xiàn)邏輯基本上體現(xiàn)在如下所示的代碼片段中。一個(gè)ConfigurationRoot對(duì)象維護(hù)著一組提供原始配置數(shù)據(jù)的ConfigurationProvider對(duì)象,每一次對(duì)配置數(shù)據(jù)的讀寫操作最終都會(huì)轉(zhuǎn)移到它們頭上。當(dāng)調(diào)用它的索引指定相應(yīng)的Key獲取對(duì)應(yīng)配置項(xiàng)的值時(shí),我們會(huì)將這組ConfigurationProvider對(duì)象進(jìn)行逆向排序,并將指定的Key作為參數(shù)依次調(diào)用每個(gè)ConfigurationProvider的TryGet方法直到該方法返回True,并將這個(gè)方法返回的值最為索引最終的返回值。當(dāng)我們利用索引對(duì)指定配置項(xiàng)的值進(jìn)行設(shè)置的時(shí)候,實(shí)際上會(huì)調(diào)用每個(gè)ConfigurationProvider的Set方法。

1: public class ConfigurationRoot: IConfigurationRoot 2: { 3: private IList<IConfigurationProvider> providers; 4:? 5: public ConfigurationRoot(IList<IConfigurationProvider> providers) 6: { 7: this.providers = providers; 8: providers.ForEach(provider => provider.Load()); 9: } 10:? 11: public string this[string key] 12: { 13: get 14: { 15: string value = null; 16: return providers.Reverse().Any(p => p.TryGet(key, out value)) 17: ? value: null; 18: } 19: set 20: { 21: providers.ForEach(provider => provider.Set(key, value)); 22: } 23: } 24:? 25: public IEnumerable<IConfigurationSection> GetChildren()=> this.GetChildren(null); 26:? 27: public IConfigurationSection GetSection(string key) => new ConfigurationSection(this, key); 28:? 29: public void Reload() => providers.ForEach(provider => provider.Load()); 30:? 31: internal IEnumerable<IConfigurationSection> GetChildrenCore(string path) 32: { 33: return providers.Aggregate(Enumerable.Empty<string>(), 34: (seed, source) => source.GetChildKeys(seed, path, ":")) 35: .Distinct() 36: .Select(key => GetSection(ConfigurationPath.Combine(path, key))); 37: } 38: //其它成員 39: }

ConfigurationRoot實(shí)現(xiàn)在索引中讀取配置的邏輯體現(xiàn)了配置模型一個(gè)重要的特性,那就是如果某個(gè)配置項(xiàng)的數(shù)據(jù)具有多個(gè)來源,那么最后添加到ConfigurationBuilder中的ConfigurationProvider具有更高的優(yōu)先級(jí),我們姑且將這個(gè)特性稱為ConfigurationProvider“后來居上”的原則。如果希望覆蓋應(yīng)用現(xiàn)有的某個(gè)配置,我們只需要將提供新配置的ConfigurationProvider添加到ConfigurationBuilder之上即可。

我們定義了一個(gè)輔助方法GetChildrenCore來獲取某個(gè)配置節(jié)的所有子配置節(jié),這個(gè)指定的配置節(jié)通過作為參數(shù)的路徑來表示。當(dāng)這個(gè)方法執(zhí)行之后,所有ConfigurationProvider的GetChildKeys方法會(huì)被調(diào)用以獲取所有子配置節(jié)的Key,我們利用它們生成表示配置節(jié)的ConfigurationSection對(duì)象。在實(shí)現(xiàn)的GetChildren方法中,我們會(huì)調(diào)用這個(gè)方法來獲取隸屬于自己的所有子配置節(jié)。而另一個(gè)GetSection方法中,我們直接返回根據(jù)指定路徑(對(duì)于表示根配置節(jié)來說,參數(shù)key表示配置節(jié)的路徑)創(chuàng)建的ConfigurationSection對(duì)象。

ConfigurationSection

在上面關(guān)于用于模擬ConfigurationRoot類型定義的代碼中我們知道最終表示非根配置節(jié)的ConfigurationSection對(duì)象是根據(jù)它的路徑和作為根配置節(jié)的ConfigurationRoot對(duì)象創(chuàng)建的。ConfigurationRoot將配置的讀寫操作遞交給相應(yīng)的ConfigurationProvider來完成,而ConfigurationSection則將委托自己的根配置節(jié)來完成讀寫配置的操作,這樣的策略體現(xiàn)在如下所示的代碼中。

1: public class ConfigurationSection: IConfigurationSection 2: { 3: private ConfigurationRoot root; 4: private string key; 5:? 6: public string Key 7: { 8: get { return key ?? (key = ConfigurationPath.GetSectionKey(this.Path)); } 9: } 10: public string Path { get; private set; } 11: public string Value { get; set; } 12: public string this[string key] 13: { 14: get 15: { 16: return root[this.Path]; 17: } 18:? 19: set 20: { 21: root[this.Path] = Value; 22: } 23: } 24:? 25: public ConfigurationSection(ConfigurationRoot root, string path) 26: { 27: this.root = root; 28: this.Path = path; 29: } 30:? 31: public IConfigurationSection GetSection(string key) => root.GetSection(ConfigurationPath.Combine(this.Path, key)); 32: public IEnumerable<IConfigurationSection> GetChildren() => root.GetChildren(this.Path); 33: //其它成員 34: }

[1] ForEach是為了讓代碼盡量精練而為類型IEnumerable<T>定義的擴(kuò)展方法,在后續(xù)章節(jié)中我們會(huì)經(jīng)常使用到它。

?

ASP.NET Core的配置(1):讀取配置信息
ASP.NET Core的配置(2):配置模型詳解
ASP.NET Core的配置(3): 將配置綁定為對(duì)象[上篇]
ASP.NET Core的配置(3): 將配置綁定為對(duì)象[下篇]
ASP.NET Core的配置(4):多樣性的配置源[上篇]
ASP.NET Core的配置(4):多樣性的配置源[中篇]
ASP.NET Core的配置(4):多樣性的配置源[下篇]
ASP.NET Core的配置(5):配置的同步[上篇]
ASP.NET Core的配置(5):配置的同步[下篇]

轉(zhuǎn)載于:https://www.cnblogs.com/artech/p/asp-net-core-config-02.html

總結(jié)

以上是生活随笔為你收集整理的ASP.NET Core的配置(2):配置模型详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。