重新整理 .net core 实践篇————配置系统之盟约[五]
前言
在asp .net core 中我们会看到一个appsettings.json 文件,它就是我们在服务中的各种配置,是至关重要的一部门。
不管是官方自带的服务,还是我们自己编写的服务都是用它来实现自己服务的动态配置,这就是约定。
配置文件之所以会成为约定,最主要的原因就是好用,不然可能第三方的配置文件管理的就会出来替代官方的配置文件管理系统,官方也提供了对应的接口来让第三方接入。
正文
官方提供了 Microsoft.Extensions.Configuration.Abstration接口。
同时提供了Microsoft.Extensions.Configuration 来作为实现。
所以我们如果想自己写第三方的包,那么就可以对 Microsoft.Extensions.Configuration.Abstration 的某一部分或者全部实现。
初学.net core的时候,陷入了一个误区,当时认为配置系统就是从json中进行读取,实际上配置系统是可以从命令行中获取、从环境变量中获取,只要他们符合key-value这种字符串键值对的方式。
配置文件系统有四个主要的接口,也可以理解为四个主要的模块功能。后面用代码解释一下这几个的作用。
IConfiguration
IConfigurationRoot
IConfigurationSection
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 实践篇————配置系统之盟约[五]的更多相关文章
- 重新整理 .net core 实践篇—————配置系统之军令状[七](配置文件)
前言 介绍一下配置系统中的配置文件,很多服务的配置都写在配置文件中,也是配置系统的大头. 正文 在asp .net core 提供了下面几种配置文件格式的读取方式. Microsoft.extensi ...
- 重新整理 .net core 实践篇—————配置系统之间谍[八](文件监控)
前言 前文提及到了当我们的配置文件修改了,那么从 configurationRoot 在此读取会读取到新的数据,本文进行扩展,并从源码方面简单介绍一下,下面内容和前面几节息息相关. 正文 先看一下,如 ...
- 重新整理 .net core 实践篇—————配置系统之强类型配置[十]
前言 前文中我们去获取value值的时候,都是通过configurationRoot 来获取的,如configurationRoot["key"],这种形式. 这种形式有一个不好的 ...
- 重新整理 .net core 实践篇————配置系统——军令(命令行)[六]
前言 前文已经基本写了一下配置文件系统的一些基本原理.本文介绍一下命令行导入配置系统. 正文 要使用的话,引入Microsoft.extensions.Configuration.commandLin ...
- 重新整理 .net core 实践篇—————配置系统之简单配置中心[十一]
前言 市面上已经有很多配置中心集成工具了,故此不会去实践某个框架. 下面链接是apollo 官网的教程,实在太详细了,本文介绍一下扩展数据源,和简单翻翻阅一下apollo 关键部分. apollo 服 ...
- 重新整理 .net core 实践篇————配置应用[一]
前言 本来想整理到<<重新整理.net core 计1400篇>>里面去,但是后来一想,整理 .net core 实践篇 是偏于实践,故而分开. 因为是重新整理,那么就从配置开 ...
- 重新整理 .net core 实践篇—————日志系统之战地记者[十五]
前言 本节开始整理日志相关的东西.先整理一下日志的基本原理. 正文 首先介绍一下包: Microsoft.Extengsion.Logging.Abstrations 这个是接口包. Microsof ...
- 重新整理 .net core 实践篇—————日志系统之作用域[十七]
前言 前面介绍了服务与日志之间的配置,那么我们服务会遇到下面的场景会被遇到一些打log的问题. 前面我提及到我们的log,其实是在一个队列里面,而我们的请求是在并发的,多个用户同时发送请求这个时候我们 ...
- 重新整理 .net core 实践篇—————日志系统之结构化[十八]
前言 什么是结构化呢? 结构化,就是将原本没有规律的东西进行有规律话. 就比如我们学习数据结构,需要学习排序然后又要学习查询,说白了这就是一套,没有排序,谈如何查询是没有意义的,因为查询算法就是根据某 ...
随机推荐
- mysql 遇到的问题
1) 客户端(Navicat)远程登录操作再遇问题1142-create command denied to user×××的解决GRANT SELECT,INSERT,UPDATE,DELETE,C ...
- redis实际应用-限流
为什么要做限流 首先让我们先看一看系统架构设计中,为什么要做"限流". 旅游景点通常都会有最大的接待量,不可能无限制的放游客进入,比如故宫每天只卖八万张票,超过八万的游客,无法买票 ...
- Mysql下可能存在注入的点。
总结下mysql下可能存在注入的点,适用于mssql和oracle,先写语句,以后再写语句可能出现在哪些场景下: 针对查询: select * from x where id=* select * f ...
- 基于MVC框架的JavaWeb网站开发demo项目(JSP+Servlet+JavaBean)
1.环境配置 Windows10+Eclipse2020+jdk8+Tomcat9+MySQL8+Navicat10 2.需求分析 ①用户登录注册注销(查找.增加) ②显示用户列表(查找) ③显示用户 ...
- 906. Super Palindromes
Let's say a positive integer is a superpalindrome if it is a palindrome, and it is also the square o ...
- 【DataBase】SQL45 Training 45题训练
视频地址: https://www.bilibili.com/video/BV1pp4y1Q7Yv 创建案例库: ------------创建数据库--------------- create dat ...
- 9.while循环
while 循环 语法: while( 布尔表达式 ) { //循环内容 } 布尔值为 true 开始循环,为 false 结束循环 死循环:布尔值一直为 true. 例: package com.w ...
- 【原创】JVM如何运行Java程序的?
[Deerhang] 我们知道Java程序的运行是依赖于JVM虚拟机的,JVM类语言经过编译生成class字节码文件,字节码又经JVM进一步的编译生成机器码,最终运行在硬件上.那么JVM存在的意义是什 ...
- 微信小程序中的常见弹框
显示加载中的提示框 wx.showLoading() 当我们正在在进行网络请求时,常常就需要这个提示框 手动调用wx.hideLoading()方法才能够关闭这个提示框,通常在数据请求完毕时就应该关闭 ...
- IOC随笔小记录
对IOC的一点学习笔记 IOC (Inversion of Control):控制反转 DI (Dependency Injection):依赖注入 1.在没有使用IOC的情况下是如何进行的 在Use ...