.Net Core 中 IConfigurationSource、IConfigurationProvider、IConfigurationBuilder、IConfiguration的关系

目录

1 .Net中配置系统的缺点

2 .Net Core中新的配置体系

3 IConfigurationProvider 是个啥

4 配置对象是如何创建的


1 .Net中配置系统的缺点

以前版本的.NET有一个相对简单的配置系统,所有设置都采用带有.config扩展名的XML文件;它有一个可以处理系统设置和无类型键值对的基本模式;此外它在一定程度上支持继承,因为某些设置可以在机器范围内定义,然后在每个应用程序中被覆盖,甚至在IIS的虚拟应用程序中也可以覆盖机器范围的全局配置。通过编写和注册.NET类,还可以定义具有类型和复杂结构的自定义设置。
然而,尽管看起来很方便,但事实证明它有其局限性,即:

  • 仅支持XML文件;
  • 不同环境(开发,QA,生产等)很难有不同的配置文件/配置节;
  • 配置更改时无法接收通知;
  • 保存更改很棘手。

此外,由于依赖注入不是核心.NET基础结构的一部分,因此无法自动将配置值注入其服务中。

2 .Net Core中新的配置体系

为了解决上述缺点,微软重点重构了.NET Core 中的配置体系,并以非常灵活、可扩展的方式实现。 这一切都始于一个 builder 实例;我们向它添加配置提供器 provider。当我们需要时,我们只需要用它构建一个配置对象,它保存着从内存中加载的每个 provider 的所有值。
此配置对象能够透明地从添加的所有 provider 中返回设置信息,这意味着无论配置源是什么,我们都使用相同的语法来查询配置选项。 它加载所有注册的 provider 的注册项,并在内存中保存。你还可以在内存中更改或添加配置项。
.NET Core 中配置API的基类模型如下所示:

Provider 机制包括两个基本接口及其实现:

  • IConfigurationSource:负责创建 IConfigurationProvider 的具体实例。
  • IConfigurationProvider:定义 配置查询、重新加载以及其他具体功能的 行为约定。实现此接口的根类是ConfigurationProvider。此外还有一个作为所有基于文件的 provider 的特殊基类:FileConfigurationProvider。

ConfigurationBuilder 类本身只是 IConfigurationBuilder 接口的特定实现,此外没有其他的实现。 它约定了我们如何添加 provider 并从中构建配置对象:

var builder = new ConfigurationBuilder()
    .Add(source1)
    .Add(source2);
var cfg = builder.Build();

ConfigurationBuilder 类的 Add() 方法为该类的 IList<IConfigurationSource> 属性添加一个 source;然后调用 Build() 方法,该方法将所有的配置源创建一个 provider 集合,再根据 provider 集合实例化一个 IConfigurationRoot 实例。

关于配置对象,有三个基本接口:

  • IConfiguration 接口:提供了查询、设置配置项、监控变化等方法。
  • IConfigurationRoot 接口:在IConfiguration接口基础上,增加了 Reload 方法强制从 provider 中重新加载配置值。
  • IConfigurationSection 接口:这是一个配置节,它可以位于配置根目录下的某个位置。它定义了一个 path 属性(到达该配置节所有父节的键的顺序组合)和一个 key 属性(自己的健)。

3 IConfigurationProvider 是个啥

现在我们知道了 IConfigurationSource 是数据源的抽象,例如 Json 数据源、环境变量、内存变量等等,当然微软也提供了 XML 文件等多种数据源的实现。我们也知道了 IConfigurationBuilder 是负责从注册到其中的多个 IconfigurationSource 创建 IConfiguration 实例的接口。那么 IConfigurationProvider 是干什么的呢?它为何存在呢?

上文中提到过,.Net Core 中的配置模型(即IConfiguration)是统一的,用户采用相同的方式获取所有不同类型的配置内容。实际上所有来源不同的 IConfigurationSource 都被转换成了 统一的键值对模型 供用户使用。而 IConfigurationProvider 就是使得不同配置源变得对用户透明的“中间商”。IConfigurationProvider 规定了配置项的获取、设置、重载等统一的行为,见它的签名:

public interface IConfigurationProvider
{
        /// <summary>
        /// Tries to get a configuration value for the specified key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        /// <returns><c>True</c> if a value for the specified key was found, otherwise <c>false</c>.</returns>
        bool TryGet(string key, out string value);

        /// <summary>
        /// Sets a configuration value for the specified key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        void Set(string key, string value);

        /// <summary>
        /// Returns a change token if this provider supports change tracking, null otherwise.
        /// </summary>
        /// <returns></returns>
        IChangeToken GetReloadToken();

        /// <summary>
        /// Loads configuration values from the source represented by this <see cref="IConfigurationProvider"/>.
        /// </summary>
        void Load();

        /// <summary>
        /// Returns the immediate descendant configuration keys for a given parent path based on this
        /// <see cref="IConfigurationProvider"/>'s data and the set of keys returned by all the preceding
        /// <see cref="IConfigurationProvider"/>s.
        /// </summary>
        /// <param name="earlierKeys">The child keys returned by the preceding providers for the same parent path.</param>
        /// <param name="parentPath">The parent path.</param>
        /// <returns>The child keys.</returns>
        IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath);
}

对于任何一个 IConfigurationSource, 必须要同时实现相应的 IConfigurationProvider 接口,才能被IConfigurationBuilder创建成统一的IConfiguration对象(见下文)。

4 配置对象是如何创建的

在 .Net Core 2.x 及以上版本中,IConfiguration 服务被框架自动注册到 DI 容器中了(见 IWebHostBuilder.Build()的实现)。我们可以直接通过 IServiceProvider 或者 构造函数 获取配置对象。那么配置对象是如何创建的呢?

一般而言,我们 首先创建 IConfigurationSource 对象,调用 IConfigurationBuilder 的 Add() 方法把创建的 source 注册到 builder 对象中,最后调用 IConfigurationBuilder 的 Build() 方法创建配置对象。

Build方法做了如下工作:

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);
}

它使用所有注册的 source 创建一个 provider 集合,由于 provider 提供了统一的数据访问方式,因此就可以调用 ConfigurationRoot 的构造函数,在初始化 ConfigurationRoot 过程中,调用 provider的 Load()方法加载配置项 。

此外,对于常用的配置类型,还可以调用 IConfigurationBuilder 的一些对应的扩展方法来直接注册数据源,例如,可直接调用其AddJsonFile()扩展方法添加一个Json配置源。

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值