既然决定了开始写博客,那就从读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
这一部分细化阅读一下。