.Net Core中的配置文件源码解析

一、配置简述

  之前在.Net Framework平台开发时,一般配置文件都是xml格式的Web.config,而需要配置其他格式的文件就需要自己去读取内容,加载配置了。.而Net Core支持从命令行、环境变量、文件、内存、Key-per-file中加载配置,其中文件包括xml、ini、json三种文件格式。这里需要说明一下,不论哪种格式的配置文件,加载到程序中最终会以Key/Value形式保存,源码中将所有配置读取出来并保存在  Dictionary<string, string> Data  的字典中。额外说明一下Key-per-file配置方式是以文件名称为key,文件内容为value的形式。

二、源码解析

  点击查看源码,我画了一个主要类之间的逻辑关系图,如下:

实线表示继承关系,每条虚线表示意义已经在上面表明。可以分为4个部分,最终要构建的就是IConfigurationRoot,因为所有的配置都存储在它里面的Providers集合中的Data的字典中。

构建它两条路径,一条通过IConfigurationSource构建IConfigurationProvider,然后通过IConfigurationProvider集合构建IConfigurationRoot,也就是图上标记的1/2两步。

另一条是通过IConfigurationSource集合构建IConfigurationBuilder,在通过Builder方法遍历循环创建创建IConfigurationProvider集合,在通过IConfigurationProvider集合构建IConfigurationRoot。也就是图上的3/4两步。这里只讲文件配置方式,包括xml、ini、json文件。

IConfigurationSource:

  它是配置文件的根本,它表示配置文件本身,比如继承自它的 FileConfigurationSource 里面有个Path属性,表示文件路径,会通过这个Path读取这个文件内容。如我们常用的xml、json、ini文件配置都继承自  FileConfigurationSource 。 

IConfigurationProvider:

  前面说了,配置最终会转换为key/value的形式,而 IConfigurationProvider 就是这个装换。比如继承他的 FileConfigurationProvider ,它的构造函数需要传第一个  FileConfigurationSource   ,  FileConfigurationProvider   会根据  FileConfigurationSource   的 path 属性找到对应文件,然后读取配置文件到它的  IDictionary<string, string> Data  中。xml的读取在  XmlConfigurationProvider  中,json文件的读取在  JsonConfigurationProvider  。

        private void Load(bool reload)
        {
            //删除一些判断逻辑
            if (reload)
            {
                Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            }
            using (var stream = file.CreateReadStream())
            {
          //这一步会将文件里面的配置 放到Data中 不同的文件格式Load方法都有对应的实现,这里就不细看了。
                 Load(stream);
            }
           
            OnReload();
        } 

  一个 IConfigurationProvider 的实现类中会包含一个对应的  IConfigurationSource  的实现类,比如  JsonConfigurationProvider  类中包含一个 JsonConfigurationProvider  

IConfigurationRoot:

  所有的类最终构建的目标就是IConfigurationRoot,它包含一个 IList<IConfigurationProvider> _providers 集合,而每一个  IConfigurationProvider  包含一个  IDictionary<string, string> Data  ,所以现在你现在是不是有提花灌顶的感觉。这样做的目的是一个程序可能有多个配置文件,可能有一个xml文件、一个json文件、一个Ini文件。每个配置文件会被读取到对应的  ConfigurationSource  中,然后通过它构建对应的  ConfigurationProvider  ,然后用三个  ConfigurationProvider  构建  ConfigurationRoot  。

那这样会有一个问题,比如xml文件配置了 A的值为1,json文件也配置名称为A的值为2,那会获取谁的值呢? 下面源码可以看到,IConfigurationRoot会反转添加的顺序,循环遍历,如果找到就返回,所以后面添加的会覆盖前面添加的。

        public string this[string key]
        {
            get
            {  //_providers也就是IConfigurationRoot里面的IList<IConfigurationProvider>
                foreach (var provider in _providers.Reverse())
                {
                    string value;

                    if (provider.TryGet(key, out value))
                    {
                        return value;
                    }
                }
                return null;
            }
            set
            {
                if (!_providers.Any())
                {
                    throw new InvalidOperationException(Resources.Error_NoSources);
                }
                foreach (var provider in _providers)
                {
                    provider.Set(key, value);
                }
            }
        }

 对于Json、xml、ini文件,比如

{
  "user": {
    "address": {
      "Provice": "浙江省",
      "city": "杭州市"
    },
    "name": "张三"
  }
}

在Data中存储的key为 user:address:Province、user:address:city和user:name。中间以“:”分割,这个“:”是固定只读的,不可以改变的。

三、简单使用

  创建一个WebApi项目,修改Program代码如下:

        public static void Main(string[] args)
        {
            var configRoot = new ConfigurationBuilder()
                               .SetBasePath(Directory.GetCurrentDirectory())//设置基础路径
                               .AddJsonFile("a.json")//加载配置
                               .AddXmlFile("b.xml")
                               .Build();

            var builder = new WebHostBuilder()
                            .UseConfiguration(configRoot)
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseKestrel()
                            .UseStartup<Startup>()
                            .Build();
            builder.Run();
        }

  然后当用的时候,只需要注入 IConfiguration ,就能获取所有的配置。

posted @ 2019-05-27 08:51  MicroHeart!  阅读(...)  评论(... 编辑 收藏