既然決定了開始寫博客,那就從讀ASP.NET core的源碼開始吧!對於我這個不怎麼善於寫文章的人來講,也算是鍛鍊鍛鍊本身概括總結能力。git
俗話說,「千里之行,始於足下」,咱們先看看ASP.NET core的Configuration
是如何使用的。爲了方便說明,這裏創建的xunit
的測試工程,使用nuget
下載Microsoft.Extensions.Configuration
包:github
var dc = new Dictionary<string, string> { {"SectionA", "ValueA" }, {"SectionB", "ValueB" } }; var builder = new ConfigurationBuilder().AddInMemoryCollection(dc); var configurationRoot = builder.Build(); Assert.Equal("ValueA", configurationRoot["SectionA"]); Assert.Equal("ValueB", configurationRoot["SectionB"]);
最簡單的幾行代碼,能夠看出配置文件最基本的使用流程。c#
構建一個
ConfigurationBuilder
對象ide添加配置資源(這裏使用的是
InMemoryCollection
)函數最後使用
Build()
方法生成IConfigurationRoot
配置對象測試使用
IConfigurationRoot
對象獲取配置信息ui
這裏咱們從這段代碼中能看出ConfigurationBuilder
是繼承了IConfigurationBuilder
這個接口,它Build
出來的對象繼承了IConfigurationRoot
接口,IConfigurationRoot
又繼承了IConfiguration
接口,而這些接口都位於Microsoft.Extensions.Configuration.Abstractions
這個程序集中。this
那就從這裏開始抽絲剝繭,深刻探究下配置部分的源碼吧!請從Github下載好源碼。調試
https://github.com/aspnet/Configuration => https://github.com/dotnet/extensionsrest
你是否是發現最新的src文件夾下根本沒有咱們想要的Microsoft.Extensions
等源碼呀!咱們須要切換到v3.1.3
或如下的版本而後下載對應版本的代碼就行了。爲了更方便調試代碼,我建議仍是將代碼從Github的倉庫clone下來,構建源碼也比較簡單。
git clone https://github.com/dotnet/extensions.git
git checkout v3.1.2
restore.cmd
build.cmd
Microsoft.Extensions.Configuration.Abstraction
開始 不說廢話,上圖:
從上圖能夠看出咱們的IConfigurationBuilder
中包含一個IList<IConfigurationSource>
對象和Add(IConfigurationSource source)
方法 ,這意味着ASP.NET core的配置對象是支持多配置源的。 IConfigurationBuilder
經過Build
方法,生成一個IConfigurationRoot
對象,可經過實現父接口IConfiguration
的索引器this[string key]
獲取到配置信息。對於IConfigurationProvider
對象和IConfigurationSection
咱們經過對抽象的實現去探索。
Microsoft.Extensions.Configuration
ConfigurationBuilder
`Build`方法最終生成了`ConfigurationRoot`對象,並初始了`List<IConfigurationProvider>`。
public IConfigurationRoot Build() { var providers = new List<IConfigurationProvider>(); foreach (var source in Sources) { var provider = source.Build(this); providers.Add(provider); } return new ConfigurationRoot(providers); }
而IConfigurationProvider
是由IConfigurationSource
build生成的。
public IConfigurationBuilder Add(IConfigurationSource source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } Sources.Add(source); return this; }
那配置信息時如何加載進去的呢?
咱們先看下配置信息是如何讀取的,在索引器的get
方法中,數據時是從provider
中獲取到的,並且在全部的_providers
中倒序查找到provider
後就會退出查找,這也意味着咱們在添加多個配置源時,最後添加的配置源會覆蓋以前添加的配置源,固然這是在鍵值相同的狀況下。
public string this[string key] { get { for (var i = _providers.Count - 1; i >= 0; i--) { var provider = _providers[i]; if (provider.TryGet(key, out var value)) return value; } return null; } set... }
那provider
的數據是從哪裏來的呢?在ConfigurationProvider
這個類中,能夠看到全部的數據都來源於Data
這個Dictionary<string, string>
字典集合。
這個字典集合是何時加載的呢?
public virtual bool TryGet(string key, out string value) => Data.TryGetValue(key, out value);
在構造ConfigurationRoot
對象時,咱們看到全部的provider
對象都調用了Load()
方法。在ConfigurationProvider
類中Load()
方法是虛方法,且沒有找到別的地方對Data
這個變量進行賦值,那麼這個時候能夠猜測ConfigurationProvider
繼承類會重寫這個方法,加載Data
的值,那麼咱們去MemoryConfigurationProvider
這個類中去驗證一下,MemoryConfigurationProvider
在構造函數中完成了Data
的賦值,沒有重寫這個方法。汗~~~
public ConfigurationRoot(IList<IConfigurationProvider> providers) { if (providers == null) { throw new ArgumentNullException(nameof(providers)); } _providers = providers; _changeTokenRegistrations = new List<IDisposable>(providers.Count); foreach (var p in providers) { p.Load(); _changeTokenRegistrations.Add(ChangeToken.OnChange(() =>p.GetReloadToken(), () => RaiseChanged())); } }
那在這裏先去漫遊一下,經過FileConfigurationProvider
=> JsonConfigurationProvider
找到了Data = JsonConfigurationFileParser.Parse(stream)
,證實咱們的猜測仍是沒有錯的。
這麼一路抽絲剝繭,咱們就知道了配置信息是如何運做的了!下一步咱們看看配置文件是如何監視文件變化的,也對FileConfigurationProvider
這一部分細化閱讀一下。