前言

在asp .net core 中我们会看到一个appsettings.json 文件,它就是我们在服务中的各种配置,是至关重要的一部门。

不管是官方自带的服务,还是我们自己编写的服务都是用它来实现自己服务的动态配置,这就是约定。

配置文件之所以会成为约定,最主要的原因就是好用,不然可能第三方的配置文件管理的就会出来替代官方的配置文件管理系统,官方也提供了对应的接口来让第三方接入。

正文

官方提供了 Microsoft.Extensions.Configuration.Abstration接口。

同时提供了Microsoft.Extensions.Configuration 来作为实现。

所以我们如果想自己写第三方的包,那么就可以对 Microsoft.Extensions.Configuration.Abstration 的某一部分或者全部实现。

初学.net core的时候,陷入了一个误区,当时认为配置系统就是从json中进行读取,实际上配置系统是可以从命令行中获取、从环境变量中获取,只要他们符合key-value这种字符串键值对的方式。

配置文件系统有四个主要的接口,也可以理解为四个主要的模块功能。后面用代码解释一下这几个的作用。

  1. IConfiguration

  2. IConfigurationRoot

  3. IConfigurationSection

  4. IConfigurationBuilder

配置扩展点:

1.IConfigurationSource

2.IConfigurationProvider

扩展上诉两个可以帮助扩展不同配置来源。

static void Main(string[] args)
{
IConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddInMemoryCollection(new Dictionary<string,string>()
{
{"key1","value1"},
{"key2","value2"},
});
IConfigurationRoot configurationRoot = builder.Build(); Console.WriteLine(configurationRoot["key1"]);
Console.WriteLine(configurationRoot["key2"]);
}

看下ConfugurationBuilder:

/// <summary>
/// Used to build key/value based configuration settings for use in an application.
/// </summary>
public class ConfigurationBuilder : IConfigurationBuilder
{
/// <summary>
/// Returns the sources used to obtain configuration values.
/// </summary>
public IList<IConfigurationSource> Sources { get; } = new List<IConfigurationSource>(); /// <summary>
/// Gets a key/value collection that can be used to share data between the <see cref="IConfigurationBuilder"/>
/// and the registered <see cref="IConfigurationProvider"/>s.
/// </summary>
public IDictionary<string, object> Properties { get; } = new Dictionary<string, object>(); /// <summary>
/// Adds a new configuration source.
/// </summary>
/// <param name="source">The configuration source to add.</param>
/// <returns>The same <see cref="IConfigurationBuilder"/>.</returns>
public IConfigurationBuilder Add(IConfigurationSource source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
} Sources.Add(source);
return this;
} /// <summary>
/// Builds an <see cref="IConfiguration"/> with keys and values from the set of providers registered in
/// <see cref="Sources"/>.
/// </summary>
/// <returns>An <see cref="IConfigurationRoot"/> with keys and values from the registered providers.</returns>
public IConfigurationRoot Build()
{
var providers = new List<IConfigurationProvider>();
foreach (IConfigurationSource source in Sources)
{
IConfigurationProvider provider = source.Build(this);
providers.Add(provider);
}
return new ConfigurationRoot(providers);
}
}

在看一个东西的功能的时候一定要看下顶部这句话:

/// <summary>
/// Used to build key/value based configuration settings for use in an application.
/// </summary>
public class ConfigurationBuilder : IConfigurationBuilder

翻译过来就是用于构建在应用程序中使用的基于键/值的配置设置。

那么这个时候我们可以重点看下build,毕竟是用来构建的。

从上述语义中,大概知道是IConfigurationSource 转换为 IConfigurationProvider。

那么重点看下这两个。

先看IConfigurationSource 接口。

/// <summary>
/// Represents a source of configuration key/values for an application.
/// </summary>
public interface IConfigurationSource
{
/// <summary>
/// Builds the <see cref="IConfigurationProvider"/> for this source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>An <see cref="IConfigurationProvider"/></returns>
IConfigurationProvider Build(IConfigurationBuilder builder);
}

表示一个配置文件的来源。里面只有一个方法就是Provider,这时候猜想IConfigurationProvider就是用于统一获取值的方式的。

看下Provider:

/// <summary>
/// Provides configuration key/values for an application.
/// </summary>
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>The change token.</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);
}

查看头部:

/// <summary>
/// Provides configuration key/values for an application.
/// </summary>
public interface IConfigurationProvider

为应用提供key value配置。

那么这时候猜想ConfigurationRoot就是来整合配置的。

先看我们上文一组IConfigurationSource,IConfigurationProvider的具体实现MemoryConfigurationSource 和 MemoryConfigurationProvider。

MemoryConfigurationSource :

/// <summary>
/// Represents in-memory data as an <see cref="IConfigurationSource"/>.
/// </summary>
public class MemoryConfigurationSource : IConfigurationSource
{
/// <summary>
/// The initial key value configuration pairs.
/// </summary>
public IEnumerable<KeyValuePair<string, string>> InitialData { get; set; } /// <summary>
/// Builds the <see cref="MemoryConfigurationProvider"/> for this source.
/// </summary>
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
/// <returns>A <see cref="MemoryConfigurationProvider"/></returns>
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new MemoryConfigurationProvider(this);
}
}

MemoryConfigurationProvider:

/// <summary>
/// In-memory implementation of <see cref="IConfigurationProvider"/>
/// </summary>
public class MemoryConfigurationProvider : ConfigurationProvider, IEnumerable<KeyValuePair<string, string>>
{
private readonly MemoryConfigurationSource _source; /// <summary>
/// Initialize a new instance from the source.
/// </summary>
/// <param name="source">The source settings.</param>
public MemoryConfigurationProvider(MemoryConfigurationSource source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
} _source = source; if (_source.InitialData != null)
{
foreach (KeyValuePair<string, string> pair in _source.InitialData)
{
Data.Add(pair.Key, pair.Value);
}
}
} /// <summary>
/// Add a new key and value pair.
/// </summary>
/// <param name="key">The configuration key.</param>
/// <param name="value">The configuration value.</param>
public void Add(string key, string value)
{
Data.Add(key, value);
} /// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return Data.GetEnumerator();
} /// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

所以我们如果要扩展的来源的话,需要实现IConfigurationSource、IConfigurationProvider即可。

那么有时候我们会在配置文件中看到:

{
"section1:key3":"value3"
}

是否这个解释含义是section1:key3作为key然后value3作为value呢?

其实不是,这是为了能够让配置分组。

static void Main(string[] args)
{
IConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddInMemoryCollection(new Dictionary<string,string>()
{
{"key1","value1"},
{"key2","value2"},
{"section1:key3","values3"}
});
IConfigurationRoot configurationRoot = builder.Build(); Console.WriteLine(configurationRoot["key1"]);
Console.WriteLine(configurationRoot["key2"]);
Console.WriteLine(configurationRoot["section1:key3"]);
Console.WriteLine(configurationRoot.GetSection("section1")["key3"]);
}

可以看到configurationRoot 既可以把section1:key3 当作key,同时也可以把section1:key3,当作以section1为分组下面的key3。

这里我们就走进configurationRoot,看下它是如何让我们通过索引的方式获取值的。

configurationRoot 下的索引:

public string this[string key]
{
get
{
for (int i = _providers.Count - 1; i >= 0; i--)
{
IConfigurationProvider provider = _providers[i]; if (provider.TryGet(key, out string value))
{
return value;
}
} return null;
}
set
{
if (!_providers.Any())
{
throw new InvalidOperationException(SR.Error_NoSources);
} foreach (IConfigurationProvider provider in _providers)
{
provider.Set(key, value);
}
}
}

这个其实就是遍历我们的providers。那么来看下getsection部分。

public IConfigurationSection GetSection(string key)
=> new ConfigurationSection(this, key);

查看ConfigurationSection的主要部分:

public ConfigurationSection(IConfigurationRoot root, string path)
{
if (root == null)
{
throw new ArgumentNullException(nameof(root));
} if (path == null)
{
throw new ArgumentNullException(nameof(path));
} _root = root;
_path = path;
}
public string this[string key]
{
get
{
return _root[ConfigurationPath.Combine(Path, key)];
} set
{
_root[ConfigurationPath.Combine(Path, key)] = value;
}
}

实例化主要是记录section的值,并且记录IConfigurationRoot来源。

然后其索引方式还是调用了IConfigurationRoot,只是setion的值和key值做了一些处理,这个处理是ConfigurationPath来完成的。

看下ConfigurationPath:

/// <summary>
/// The delimiter ":" used to separate individual keys in a path.
/// </summary>
public static readonly string KeyDelimiter = ":"; /// <summary>
/// Combines path segments into one path.
/// </summary>
/// <param name="pathSegments">The path segments to combine.</param>
/// <returns>The combined path.</returns>
public static string Combine(params string[] pathSegments)
{
if (pathSegments == null)
{
throw new ArgumentNullException(nameof(pathSegments));
}
return string.Join(KeyDelimiter, pathSegments);
}

从上诉看,ConfigurationSection的原理还是很简单的。

getSetion 部分把前缀记录下来,然后和key值做一个拼接,还是调用ConfigurationRoot的索引部分。

经过简单的分析,我们完全可以玩套娃模式。

static void Main(string[] args)
{
IConfigurationBuilder builder = new ConfigurationBuilder();
builder.AddInMemoryCollection(new Dictionary<string,string>()
{
{"key1","value1"},
{"key2","value2"},
{"section1:key3","values3"},
{"section2:section3:key4","values4"}
});
IConfigurationRoot configurationRoot = builder.Build();
var section2 = configurationRoot.GetSection("section2");
var section3 = section2.GetSection("section3");
Console.WriteLine(section3["key4"]);
}

虽然不提倡,但是可以这么干。

下一节,配置文件之军令(命令行)

以上只是个人整理,如有错误,望请指出,谢谢。

重新整理 .net core 实践篇————配置系统之盟约[五]的更多相关文章

  1. 重新整理 .net core 实践篇—————配置系统之军令状[七](配置文件)

    前言 介绍一下配置系统中的配置文件,很多服务的配置都写在配置文件中,也是配置系统的大头. 正文 在asp .net core 提供了下面几种配置文件格式的读取方式. Microsoft.extensi ...

  2. 重新整理 .net core 实践篇—————配置系统之间谍[八](文件监控)

    前言 前文提及到了当我们的配置文件修改了,那么从 configurationRoot 在此读取会读取到新的数据,本文进行扩展,并从源码方面简单介绍一下,下面内容和前面几节息息相关. 正文 先看一下,如 ...

  3. 重新整理 .net core 实践篇—————配置系统之强类型配置[十]

    前言 前文中我们去获取value值的时候,都是通过configurationRoot 来获取的,如configurationRoot["key"],这种形式. 这种形式有一个不好的 ...

  4. 重新整理 .net core 实践篇————配置系统——军令(命令行)[六]

    前言 前文已经基本写了一下配置文件系统的一些基本原理.本文介绍一下命令行导入配置系统. 正文 要使用的话,引入Microsoft.extensions.Configuration.commandLin ...

  5. 重新整理 .net core 实践篇—————配置系统之简单配置中心[十一]

    前言 市面上已经有很多配置中心集成工具了,故此不会去实践某个框架. 下面链接是apollo 官网的教程,实在太详细了,本文介绍一下扩展数据源,和简单翻翻阅一下apollo 关键部分. apollo 服 ...

  6. 重新整理 .net core 实践篇————配置应用[一]

    前言 本来想整理到<<重新整理.net core 计1400篇>>里面去,但是后来一想,整理 .net core 实践篇 是偏于实践,故而分开. 因为是重新整理,那么就从配置开 ...

  7. 重新整理 .net core 实践篇—————日志系统之战地记者[十五]

    前言 本节开始整理日志相关的东西.先整理一下日志的基本原理. 正文 首先介绍一下包: Microsoft.Extengsion.Logging.Abstrations 这个是接口包. Microsof ...

  8. 重新整理 .net core 实践篇—————日志系统之作用域[十七]

    前言 前面介绍了服务与日志之间的配置,那么我们服务会遇到下面的场景会被遇到一些打log的问题. 前面我提及到我们的log,其实是在一个队列里面,而我们的请求是在并发的,多个用户同时发送请求这个时候我们 ...

  9. 重新整理 .net core 实践篇—————日志系统之结构化[十八]

    前言 什么是结构化呢? 结构化,就是将原本没有规律的东西进行有规律话. 就比如我们学习数据结构,需要学习排序然后又要学习查询,说白了这就是一套,没有排序,谈如何查询是没有意义的,因为查询算法就是根据某 ...

随机推荐

  1. 工厂模式之简单工厂模式,head first设计模式

    简单设计模式比较简单,15分钟的时间,跟着我做,十几分钟学会简单设计模式,开始吧: 1.使用new实例化一个对象时,你肯定知道这种代码缺少弹性,绑定着具体的类会导致代码更加脆弱,简单工厂模式就是针对接 ...

  2. 2021软工-CSDN APP分析

    项目 内容 这个作业属于哪个课程 2021春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 案例分析作业要求 我在这个课程的目标是 提升软件开发能力,提高团队协作能力 这个作业在哪个具体方面 ...

  3. VScode+Sublime安装与汉化

    两大常用编辑器安装与汉化 想敲代码?工欲善其事,必先利其器,先安装编写代码所需要的工具吧.本期的主角是VScode与Sublime,这是两款比较热门的代码编辑器. 目录: VScode安装与汉化 Su ...

  4. hdu2158 最短区间版大家来找碴

    题意:                  最短区间版大家来找碴 Problem Description 给定一个序列,有N个整数,数值范围为[0,N).有M个询问,每次询问给定Q个整数,可能出现重复值 ...

  5. hdu4990 矩阵快速幂

    题意:       给你一短代码,让你优化这个代码,代码如下 #pragma comment(linker, "/STACK:1024000000,1024000000") #in ...

  6. JAVA反序列化漏洞复现

    目录 Weblogic反序列化漏洞 Weblogic < 10.3.6 'wls-wsat' XMLDecoder 反序列化漏洞(CVE-2017-10271) Weblogic WLS Cor ...

  7. springboot 项目中css js 等静态资源无法访问的问题

    目录 问题场景 问题分析 问题解决 问题场景 今天在开发一个springboot 项目的时候突然发现 css js 等静态资源竟然都报404找不到,折腾了好久终于把问题都解决了,决定写篇博客,纪录总结 ...

  8. spring mvc简单使用

    spring mvc pom.xml依赖与插件 导入servlet.springmvc.Jackson的依赖,编译插件.tomcat <?xml version="1.0" ...

  9. VS2017报错 由#define后的分号引发的【“ 应输入“)】

    其实并不是第十行分号出现了问题,而是由于在宏定义后面加了分号,修改成这样即可 一开始竟然没看出来--甚至以为是VS中出现"宏可以转换为constexpr"问题--下次要仔细--

  10. redis中keys命令带来的线上性能问题

    起因 下午接到运维反馈,生产redis有个执行keys的命令请求太慢了,要两三秒才能响应 涉及命令如下: KEYS ttl_600::findHeadFootData-15349232-*-head ...