自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

揭開 ConfigurationManager 的面紗

開發(fā) 后端
在這個系列中,我將探索一下 .NET 6 中的一些新特性。已經有很多關于 .NET 6 的內容,包括很多來自 .NET 和 ASP.NET 團隊本身的文章。在這個系列中,我將探索一下這些特性背后的一些代碼。

在這個系列中,我將探索一下 .NET 6 中的一些新特性。已經有很多關于 .NET 6 的內容,包括很多來自 .NET 和 ASP.NET 團隊本身的文章。在這個系列中,我將探索一下這些特性背后的一些代碼。

在這第一篇文章中,來研究一下 ConfigurationManager 類,講一下為什么要新增這個類,并看一下它的的一些實現(xiàn)代碼。

1什么是 ConfigurationManager

如果你的第一反應是“什么是 ConfigurationManager”,那么不用擔心,你沒有錯過一個重要的公告:

加入 ConfigurationManager 是為了支持 ASP.NET Core 的新 WebApplication 模型,用于簡化 ASP.NET Core 的啟動代碼。然而 ConfigurationManager 在很大程度上是一個實現(xiàn)細節(jié)。它的引入是為了優(yōu)化一個特定的場景(我很快會講),但在大多數情況下,你不需要(也不會)知道你在使用它。

在我們討論 ConfigurationManager 本身之前,我們先來看看它所取代的東西和原因。

2.NET 5 中的/

.NET 5 圍繞配置暴露了多種類型,但在你的應用程序中直接使用的兩個主要類型是:

  • IConfigurationBuilder - 用來添加配置源。在構建器上調用 Build() 讀取每個配置源,并構建最終的配置。
  • IConfigurationRoot - 代表最終“構建”好的配置。

IConfigurationBuilder 接口主要是一個圍繞配置源列表的封裝器。配置提供者通常包括擴展方法(如 AddJsonFile() 和 AddAzureKeyVault()),將配置源添加到 Sources 列表中。

  1. public interface IConfigurationBuilder 
  2.     IDictionary<string, object> Properties { get; } 
  3.     IList<IConfigurationSource> Sources { get; } 
  4.     IConfigurationBuilder Add(IConfigurationSource source); 
  5.     IConfigurationRoot Build(); 

同時,IConfigurationRoot 代表最終“層”的配置值,結合了每個配置源的所有值,以提供所有配置值的最終“平面”視圖。

后者配置提供者(環(huán)境變量)覆蓋了前者配置提供者(appsettings.json、sharedsettings.json)添加的值。

在 .NET 5 及以前的版本中,IConfigurationBuilder 和 IConfigurationRoot 接口分別由 ConfigurationBuilder 和 ConfigurationRoot 實現(xiàn)。如果你直接使用這些類型,你可能會這樣做:

  1. var builder = new ConfigurationBuilder(); 
  2.  
  3. // add static values 
  4. builder.AddInMemoryCollection(new Dictionary<string, string> 
  5.     { "MyKey""MyValue" }, 
  6. }); 
  7.  
  8. // add values from a json file 
  9. builder.AddJsonFile("appsettings.json"); 
  10.  
  11. // create the IConfigurationRoot instance 
  12. IConfigurationRoot config = builder.Build(); 
  13.  
  14. string value = config["MyKey"]; // get a value 
  15. IConfigurationSection section = config.GetSection("SubSection"); //get a section 

在一個典型的 ASP.NET Core 應用程序中,你不會自己創(chuàng)建 ConfigurationBuilder,或調用 Build(),但除此之外,這就是幕后發(fā)生的事情。這兩種類型之間有明確的分離,而且在大多數情況下,配置系統(tǒng)運行良好,那么為什么我們在.NET 6 中需要一個新類型呢?

3.NET 5 中“部分構建”配置的問題

這種設計的主要問題是在你需要“部分”構建配置的時候。當你將配置存儲在 Azure Key Vault 等服務中,甚至是數據庫中時,這是一個常見的問題。

例如,以下是在 ASP.NET Core 中的 ConfigureAppConfiguration() 里面從 Azure Key Vault 讀取 secrects 的建議方式:

  1. .ConfigureAppConfiguration((context, config) => 
  2.     // "normal" configuration etc 
  3.     config.AddJsonFile("appsettings.json"); 
  4.     config.AddEnvironmentVariables(); 
  5.  
  6.     if (context.HostingEnvironment.IsProduction()) 
  7.     { 
  8.         IConfigurationRoot partialConfig = config.Build(); // build partial config 
  9.         string keyVaultName = partialConfig["KeyVaultName"]; // read value from configuration 
  10.         var secretClient = new SecretClient( 
  11.             new Uri($"https://{keyVaultName}.vault.azure.net/"), 
  12.             new DefaultAzureCredential()); 
  13.         config.AddAzureKeyVault(secretClient, new KeyVaultSecretManager()); // add an extra configuration source 
  14.         // The framework calls config.Build() AGAIN to build the final IConfigurationRoot 
  15.     } 
  16. }) 

配置 Azure Key Vault 提供者需要一個配置值,所以你陷入了一個雞和蛋的問題--在你建立配置之前,你無法添加配置源。

解決辦法是:

  • 添加“初始”配置值;
  • 通過調用 IConfigurationBuilder.Build() 構建“部分”配置結果;
  • 從生成的 IConfigurationRoot 中檢索所需的配置值;
  • 使用這些值來添加剩余的配置源;
  • 框架隱含地調用 IConfigurationBuilder.Build(),生成最終的 IConfigurationRoot 并將其用于最終的應用配置。

這整個過程有點亂,但它本身并沒有什么問題,那么缺點是什么呢?

缺點是我們必須調用 Build() 兩次:一次是只使用第一個源來構建 IConfigurationRoot,另一次是使用所有源來構建 IConfiguartionRoot,包括 Azure Key Vault 源。

在默認的 ConfigurationBuilder 實現(xiàn)中,調用 Build() 會遍歷所有的源,加載提供者,并將這些傳遞給 ConfigurationRoot 的一個新實例。

  1. public IConfigurationRoot Build() 
  2.     var providers = new List<IConfigurationProvider>(); 
  3.     foreach (IConfigurationSource source in Sources) 
  4.     { 
  5.         IConfigurationProvider provider = source.Build(this); 
  6.         providers.Add(provider); 
  7.     } 
  8.     return new ConfigurationRoot(providers); 

然后,ConfigurationRoot 依次循環(huán)遍歷這些提供者,并加載配置值。

  1. public class ConfigurationRoot : IConfigurationRoot, IDisposable 
  2.     private readonly IList<IConfigurationProvider> _providers; 
  3.     private readonly IList<IDisposable> _changeTokenRegistrations; 
  4.  
  5.     public ConfigurationRoot(IList<IConfigurationProvider> providers) 
  6.     { 
  7.         _providers = providers; 
  8.         _changeTokenRegistrations = new List<IDisposable>(providers.Count); 
  9.  
  10.         foreach (IConfigurationProvider p in providers) 
  11.         { 
  12.             p.Load(); 
  13.             _changeTokenRegistrations.Add(ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged())); 
  14.         } 
  15.     } 
  16.     // ... remainder of implementation 

如果你在應用啟動時調用 Build() 兩次,那么所有這些都會發(fā)生兩次。

一般來說,從配置源獲取數據一次以上并無大礙,但這是不必要的工作,而且經常涉及到(相對緩慢的)文件讀取等。

這是一種常見的模式,所以在 .NET 6 中引入了一個新的類型來避免這種“重新構建”,即 ConfigurationManager。

4..NET 6 中的配置管理器

作為 .NET 6 中“簡化”應用模型的一部分,.NET 團隊增加了一個新的配置類型--ConfigurationManager。這種類型同時實現(xiàn)了 IConfigurationBuilder 和 IConfigurationRoot。通過將這兩種實現(xiàn)結合在一個單一的類型中,.NET 6 可以優(yōu)化上一節(jié)中展示的常見模式。

有了 ConfigurationManager,當 IConfigurationSource 被添加時(例如當你調用 AddJsonFile() 時),提供者被立即加載,配置被更新。這可以避免在部分構建的情況下不得不多次加載配置源。

由于 IConfigurationBuilder 接口將源作為 IList 公開,因此實現(xiàn)這一點比聽起來要難一些:

  1. public interface IConfigurationBuilder 
  2.     IList<IConfigurationSource> Sources { get; } 
  3.     // .. other members 

從 ConfigurationManager 的角度來看,這個問題是 IList<> 暴露了 Add() 和 Remove() 函數。如果使用一個簡單的 List<>,消費者可以在 ConfigurationManager 不知道的情況下添加和刪除配置提供者。

為了解決這個問題,ConfigurationManager 使用一個自定義的 IList<> 實現(xiàn)。這包含對 ConfigurationManager 實例的引用,這樣任何變化都可以反映在配置中:

  1. private class ConfigurationSources : IList<IConfigurationSource> 
  2.     private readonly List<IConfigurationSource> _sources = new(); 
  3.     private readonly ConfigurationManager _config; 
  4.  
  5.     public ConfigurationSources(ConfigurationManager config) 
  6.     { 
  7.         _config = config; 
  8.     } 
  9.  
  10.     public void Add(IConfigurationSource source) 
  11.     { 
  12.         _sources.Add(source); 
  13.         _config.AddSource(source); // add the source to the ConfigurationManager 
  14.     } 
  15.  
  16.     public bool Remove(IConfigurationSource source) 
  17.     { 
  18.         var removed = _sources.Remove(source); 
  19.         _config.ReloadSources(); // reset sources in the ConfigurationManager 
  20.         return removed; 
  21.     } 
  22.  
  23.     // ... additional implementation 

通過使用一個自定義的 IList<> 實現(xiàn),ConfigurationManager 確保每當有新的源被添加時就調用 AddSource()。這就是 ConfigurationManager 的優(yōu)勢所在:調用 AddSource() 可以立即加載源:

  1. ublic class ConfigurationManager 
  2.  
  3.     private void AddSource(IConfigurationSource source) 
  4.     { 
  5.         lock (_providerLock) 
  6.         { 
  7.             IConfigurationProvider provider = source.Build(this); 
  8.             _providers.Add(provider); 
  9.  
  10.             provider.Load(); 
  11.             _changeTokenRegistrations.Add(ChangeToken.OnChange(() => provider.GetReloadToken(), () => RaiseChanged())); 
  12.         } 
  13.  
  14.         RaiseChanged(); 
  15.     } 

這個方法立即在 IConfigurationSource 上調用 Build 來創(chuàng)建 IConfigurationProvider,并將其添加到提供者列表中。

接下來,該方法調用 IConfigurationProvider.Load()。這將把數據加載到提供者中,(例如從環(huán)境變量、JSON 文件或 Azure Key Vault),這是“昂貴”的步驟,而這一切就是為了加載數據 在“正常”情況下,你只需向 IConfigurationBuilder 添加源,并可能需要多次構建它,這就給出了“最佳”方法——源被加載一次,且只有一次。

ConfigurationManager 中 Build() 的實現(xiàn)現(xiàn)在什么都沒做,只是返回它自己:

  1. IConfigurationRoot IConfigurationBuilder.Build() => this; 

當然,軟件開發(fā)是所有關于權衡的問題。如果你只添加源,那么在添加源的時候遞增構建源就很有效。然而,如果你調用任何其他 IList<> 函數,如 Clear()、Remove() 或索引器,ConfigurationManager 就必須調用 ReloadSources():

  1. private void ReloadSources() 
  2.     lock (_providerLock) 
  3.     { 
  4.         DisposeRegistrationsAndProvidersUnsynchronized(); 
  5.  
  6.         _changeTokenRegistrations.Clear(); 
  7.         _providers.Clear(); 
  8.  
  9.         foreach (var source in _sources) 
  10.         { 
  11.             _providers.Add(source.Build(this)); 
  12.         } 
  13.  
  14.         foreach (var p in _providers) 
  15.         { 
  16.             p.Load(); 
  17.             _changeTokenRegistrations.Add(ChangeToken.OnChange(() => p.GetReloadToken(), () => RaiseChanged())); 
  18.         } 
  19.     } 
  20.  
  21.     RaiseChanged(); 

正如你所看到的,如果任何一個源改變了,ConfigurationManager 必須刪除所有的東西并重新開始,迭代每個源,重新加載它們。如果你要對配置源進行大量的操作,這很快就會變得很昂貴,而且會完全否定 ConfigurationManager 的原始優(yōu)勢。

當然,刪除源是非常罕見的,所以 ConfigurationManager 是為最常見的情況而優(yōu)化的。誰能猜到呢?

下表給出了使用 ConfigurationBuilder 和 ConfigurationManager 的各種操作的相對成本的最終總結:

5.是否需關心 ConfigurationManager

那么讀了這么多,你是否應該關心你是使用 ConfigurationManager 還是 ConfigurationBuilder?

也許不應該。

在 .NET 6 中引入的新的 WebApplicationBuilder 使用 ConfigurationManager,它優(yōu)化了我上面描述的使用情況,即你需要“部分構建”你的配置。

然而,ASP.NET Core 早期版本中引入的 WebHostBuilder 或 HostBuilder 在 .NET 6 中仍然非常受支持,它們繼續(xù)在幕后使用 ConfigurationBuilder 和 ConfigurationRoot 類型。

我認為唯一需要注意的情況是,如果你在某個地方依賴 IConfigurationBuilder 或 IConfigurationRoot 作為具體類型的 ConfigurationBuilder 或 ConfigurationRoot。這在我看來是非常不太可能發(fā)生的,如果你依賴這一點,我很想知道原因。

但除了這個小眾的例外,“老”類型不會消失,所以沒有必要擔心。如果你需要進行“部分構建”,并且你使用了新的 WebApplicationBuilder,那么你的應用程序將會有更高的性能,這一點你應該感到高興。

6.總結

在這篇文章中,我描述了在 .NET 6 中引入的新的 ConfigurationManager 類型,并在最小(Minimal) API 示例中被新的 WebApplicationBuilder 所使用。引入 ConfigurationManager 是為了優(yōu)化一種常見的情況,即你需要“部分構建”配置。這通常是因為配置提供者本身需要一些配置,例如,從 Azure Key Vault 加載 secrects,需要配置表明要使用哪個 Vault 庫。

ConfigurationManager 優(yōu)化了這種情況:它在添加源時立即加載,而不是等到你調用 Build()。這就避免了在“部分構建”情況下“重建”配置的需要,其代價是其他不常見操作(如刪除一個源)可能變得更昂貴的。

 

責任編輯:武曉燕 來源: 精致碼農
相關推薦

2013-09-22 11:03:20

SocketSocket編程

2019-10-12 10:50:00

JavaScript編程語言代碼

2015-08-20 13:43:17

NFV網絡功能虛擬化

2009-09-08 16:30:18

網銀木馬

2010-05-17 09:13:35

2014-03-12 11:11:39

Storage vMo虛擬機

2021-06-07 08:18:12

云計算云端阿里云

2023-06-07 13:43:49

云計算

2010-05-26 19:12:41

SVN沖突

2009-12-03 09:19:41

Linux系統(tǒng)奧秘

2009-06-01 09:04:44

Google WaveWeb

2018-03-01 09:33:05

軟件定義存儲

2016-04-06 09:27:10

runtime解密學習

2009-09-15 15:34:33

Google Fast

2023-11-02 09:55:40

2025-01-08 15:44:04

2016-11-10 12:49:00

2025-01-07 15:07:13

2016-11-16 09:06:59

2024-02-14 09:00:00

機器學習索引ChatGPT
點贊
收藏

51CTO技術棧公眾號