前言

在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. 阿里巴巴面试-Java后端-社招5面技术总结(Offer已拿)

    最近接到阿里妈妈的面试通知,历经一个月,虽然过程挺坎坷,但总算是拿到了offer.这里简单记录下面试所遇问题,仅供各位大佬参考. 由于前面两面的时间过去的有点久了,只能根据记忆大概写些记得问题. 部门 ...

  2. Linux+MicroPython+esp8233 YES!

    MicPython MicroPython是澳大利亚程序员和物理学家Damien George在2013年一次成功的众筹活动后最初创建的.MicroPython 和 CPython 在 Python ...

  3. docker搭建简单mysql主从

    关于MySQL主从模式,如果我们直接在本机上搭建的话,是没法搭建的,只能借助于虚拟机,但有的时候我们又需要搭建一个主从集群,以便于进行一些功能性的测试.这个时候我们就可以尝试使用docker,借助于d ...

  4. SAAS云平台搭建札记: (四) AntD For React使用react-router-dom路由接收不同参数页面不刷新的问题

    在.net开发员眼里,如果使用MVC,根据路由匹配原则,可以通过各种方式接收参数,比如 /Post/List/1, /Post/List/2,或者 /Post/List?id=1,/Post/List ...

  5. Faiss源码剖析:类结构分析

    摘要:在下文中,我将尝试通过Faiss源码中各种类结构的设计来梳理Faiss中的各种概念以及它们之间的关系. 本文分享自华为云社区<Faiss源码剖析(一):类结构分析>,原文作者:HW0 ...

  6. 04- cookie与缓存技术

    什么是cookie 定义:Cookies是一种能够让网站服务器把少量数据储存到客户端的硬盘或内存,或是从客户端的硬盘读取数据的一种技术.Cookies是当你浏览某网站时,由Web服务器置于你硬盘上的一 ...

  7. 【Oauth2.0】Oauth2.0

    一.什么是Oauth2.0? 1.Oauth2.0即(Open Authorization ),Oauth2.0是一个用于第三方授权的开放标准,是Oauth1.0的升级版本,相比1.0版本易于使用: ...

  8. POJ3692 最大点权独立集元素个数

    题意:      n个男孩和m个女孩,给你他们谁和谁彼此了解,问你要找到一个集合,使得这个集合中的男孩和女孩相互了解,并且人数最多. 思路:      简单题目,其实就是在求最大点权独立集元素个数,先 ...

  9. hdu4717 三分(散点的移动)

    题意:      给你一些点,这些点有各自的初始位置,移动速度和方向,问你什么时候任意两点中最长的距离最小,求时刻和此时的距离.. 思路:      感觉题目很赞,一开始想不到三分,因为么有办法证明他 ...

  10. Redis中几种数据类型的基本操作指令

    Redis基本指令 单线程+多路IO复用技术 1. Key 指令 作用 keys * 查看当前库所有键 exists <key> 判断此键是否存在 type <key> 查看键 ...