• 抽丝剥茧读源码——Microsoft.Extensions.Configuration(1)


    开题

    ​ 既然决定了开始写博客,那就从读ASP.NET core的源码开始吧!对于我这个不怎么善于写文章的人来说,也算是锻炼锻炼自己归纳总结能力。

    千里之行,始于足下

    ​ 俗话说,“千里之行,始于足下”,我们先看看ASP.NET core的Configuration是如何使用的。为了方便说明,这里建立的xunit的测试工程,使用nuget下载Microsoft.Extensions.Configuration包:

    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"]);
    

    ​ 最简单的几行代码,可以看出配置文件最基本的使用流程。

    构建一个ConfigurationBuilder对象

    添加配置资源(这里使用的是InMemoryCollection)

    最后使用Build()方法生成IConfigurationRoot配置对象

    使用IConfigurationRoot对象获取配置信息

    ​ 这里我们从这段代码中能看出ConfigurationBuilder是继承了IConfigurationBuilder这个接口,它Build出来的对象继承了IConfigurationRoot接口,IConfigurationRoot又继承了IConfiguration接口,而这些接口都位于Microsoft.Extensions.Configuration.Abstractions这个程序集中。

    ​ 那就从这里开始抽丝剥茧,深入探究下配置部分的源码吧!请从Github下载好源码。

    https://github.com/aspnet/Configuration => https://github.com/dotnet/extensions

    ​ 你是不是发现最新的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是由IConfigurationSourcebuild生成的。

    public IConfigurationBuilder Add(IConfigurationSource source)
    {
    	if (source == null)
    	{
    		throw new ArgumentNullException(nameof(source));
    	}
    	Sources.Add(source);
    	return this;
    }
    
    ConfigurationRoot

    ​ 那配置信息时如何加载进去的呢?

    ​ 我们先看下配置信息是如何读取的,在索引器的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这一部分细化阅读一下。

    个人博客:www.corecoder.cn

  • 相关阅读:
    编写高质量的代码,改善c#程序的157个建议_之1~10
    文件指针创建失败!File *fp失败
    一拜天地
    现金流中的机会,及其评估(摘抄)
    隐藏Mac Dock 中的某个图标
    vim快捷方式
    mac 快捷键
    mysql explain
    druid 连接池配置
    分布式数据库中间件
  • 原文地址:https://www.cnblogs.com/boydenyol/p/12775255.html
Copyright © 2020-2023  润新知