实现自己的.NET Core配置Provider之Yaml
YAML是一种更适合人阅读的文件格式,很多大型的项目像Ruby on Rails都选择YAML作为配置文件的格式。如果项目的配置很少,用JSON或YAML没有多大差别。看看rails项目中的配置文件,如果用JSON写试试什么感受吧。
在《实现自己的.NET Core配置Provider之EF》中已经讲过配置的执行流程,这里不再复述,直接动手。
YamlConfigurationProvider
Yaml是基于文件的,可以直接从FileConfigurationProvider继承,在FileConfigurationProvider实现了监控文件变化并自动重新加载的功能。
internal class YamlConfigurationProvider : FileConfigurationProvider
{
public YamlConfigurationProvider(FileConfigurationSource source) : base(source)
{
}
public override void Load(Stream stream)
{
var parser = new YamlConfigurationFileParser();
Data = parser.Parse(stream);
}
}
YamlConfigurationParser是解析Yaml文件的核心,后面会介绍。
YamlConfigurationSource
internal class YamlConfigurationSource : FileConfigurationSource
{
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
EnsureDefaults(builder);
return new YamlConfigurationProvider(this);
}
}
YamlConfigurationSource实现父类的Build方法,返回YamlConfigurationProvider。
AddYamlFile扩展方法
为添加Yaml配置源增加扩展方法。
public static class YamlConfigurationExtensions
{
public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, string path)
{
return AddYamlFile(builder, provider: null, path: path, optional: false, reloadOnChange: false);
}
public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, string path, bool optional)
{
return AddYamlFile(builder, provider: null, path: path, optional: optional, reloadOnChange: false);
}
public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, string path, bool optional, bool reloadOnChange)
{
return AddYamlFile(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange);
}
public static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, IFileProvider provider, string path, bool optional, bool reloadOnChange)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException(Resources.Error_InvalidFilePath, nameof(path));
}
return builder.AddYamlFile(s =>
{
s.FileProvider = provider;
s.Path = path;
s.Optional = optional;
s.ReloadOnChange = reloadOnChange;
s.ResolveFileProvider();
});
}
internal static IConfigurationBuilder AddYamlFile(this IConfigurationBuilder builder, Action<YamlConfigurationSource> configureSource)
{
var source = new YamlConfigurationSource();
configureSource(source);
return builder.Add(source);
}
}
YamlConfigurationFileParser
解析Yaml是核心的功能,目前github有开源的C# Yaml项目:YamlDotNet和SharpYaml 。SharpYaml Fork自YamlDotNet,但做了不少改进并支持Yaml1.2,不过需要netstandard1.6+。YamlDotNet支持Yaml1.1,需要netstandard1.3+。我选择的YamlSharp。
Yaml可表示三种类型的数据:Scalar(标量,如字符串、布尔值、整数等)、Sequence(序列,如数组)和Mapping(映射,如字典,键值对等)。
关于Yaml可以参考阮一峰老师的《YAML 语言教程》。
SharpYaml会把Yaml文件转换为树形结构,然后我们只需要把所有的叶子节点的路径作为字典的键,将叶子节点的值作为字典的值存储起来就可以了。
internal class YamlConfigurationFileParser
{
private readonly IDictionary<string, string> _data = new SortedDictionary<string, string>(StringComparer.Ordinal);
private readonly Stack<string> _context = new Stack<string>();
private string _currentPath;
public IDictionary<string, string> Parse(Stream input)
{
_data.Clear();
_context.Clear();
var yaml = new YamlStream();
yaml.Load(new StreamReader(input));
if (yaml.Documents.Count > 0)
{
var rootNode = yaml.Documents[0].RootNode;
VisitYamlNode("", rootNode);
}
return _data;
}
private void VisitYamlNode(string context, YamlNode node)
{
if (node is YamlScalarNode)
{
VisitYamlScalarNode(context, (YamlScalarNode)node);
}
else if (node is YamlMappingNode)
{
VisitYamlMappingNode(context, (YamlMappingNode)node);
}
else if (node is YamlSequenceNode)
{
VisitYamlSequenceNode(context, (YamlSequenceNode)node);
}
}
private void VisitYamlScalarNode(string context, YamlScalarNode node)
{
EnterContext(context);
if (_data.ContainsKey(_currentPath))
{
throw new FormatException(string.Format(Resources.Error_KeyIsDuplicated, _currentPath));
}
_data[_currentPath] = node.Value;
ExitContext();
}
private void VisitYamlMappingNode(string context, YamlMappingNode node)
{
EnterContext(context);
foreach (var yamlNode in node.Children)
{
context = ((YamlScalarNode)yamlNode.Key).Value;
VisitYamlNode(context, yamlNode.Value);
}
ExitContext();
}
private void VisitYamlSequenceNode(string context, YamlSequenceNode node)
{
EnterContext(context);
for (int i = 0; i < node.Children.Count; i++)
{
VisitYamlNode(i.ToString(), node.Children[i]);
}
ExitContext();
}
private void EnterContext(string context)
{
if (!string.IsNullOrEmpty(context))
{
_context.Push(context);
}
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
private void ExitContext()
{
if (_context.Any())
{
_context.Pop();
}
_currentPath = ConfigurationPath.Combine(_context.Reverse());
}
}
最后
本项目已在github上开源,地址:https://github.com/chengxulvtu/Cxlt.Extensions.Configuration
在项目中使用可以执行下面的命令
Install-Package Cxlt.Extensions.Configuration.Yaml
或
dotnet add package Cxlt.Extensions.Configuration.Yaml
如果这篇文章对你有帮助或有什么问题,欢迎关注“chengxulvtu"公众号。

实现自己的.NET Core配置Provider之Yaml的更多相关文章
- 实现自己的.NET Core配置Provider之EF
<10分钟就能学会.NET Core配置>里详细介绍了.NET Core配置的用法,另外我还开源了自定义的配置Provider:EF配置Provider和Yaml配置Provider.本文 ...
- 10分钟就能学会的.NET Core配置
.NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列化为PO ...
- 【转】10分钟就能学会的.NET Core配置
.NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列化为PO ...
- SQLite EF Core Database Provider
原文链接 This database provider allows Entity Framework Core to be used with SQLite. The provider is mai ...
- Net core学习系列(九)——Net Core配置
一.简介 NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列 ...
- 关于Asp.net core配置信息读取的源码分析梳理
概述 我们都知道asp.net core配置信息的读取离不开IConfigurationSource和IConfigurationProvider这两个类,ConfigurationSource可以提 ...
- ASP.NET Core配置Kestrel 网址Urls
ASP.NET Core中如何配置Kestrel Urls呢,大家可能都知道使用UseUrls() 方法来配置. 今天给介绍全面的ASP.NET Core 配置 Urls,使用多种方式配置Urls.让 ...
- net core体系-web应用程序-4net core2.0大白话带你入门-4asp.net core配置项目访问地址
asp.net core配置访问地址 .net core web程序,默认使用kestrel作为web服务器. 配置Kestrel Urls有四种方式,我这里只介绍一种.其它方式可自行百度. 在Pr ...
- .net core 配置
.net core 配置包括很多种 例如内存变量.命令行参数.环境变量以及物理文件配置和自定义配置 物理文件配置主要有三种,它们分别是JSON.XML和INI,对应的配置源类型分别是JsonConfi ...
随机推荐
- linux之vi编辑器的基础命令
1,假如要在这个php文件的phpinfo.php;之后加入一行,我们可以先按键盘的"a",光标就会跳转到之前绿色光标之后,也就是说,"a"是代表在当前光标之后 ...
- CSS动画效果的回调
用纯JS实现动画效果代码量大,计算复杂.因此现在前端页面的动画效果一般都采用CSS来实现. CSS动画实现简单高效,但是在处理动画,控制动画过程上却缺少一些有效手段. 例如我们想在动画效果完成时调用回 ...
- chrome主页被篡改 成hao123
应该是开了个从流氓网站下的蓝灯,然后发现主页被篡改 尝试chrome设置修改无效,应该是快捷方式被改了 系统 win10 1.打开对应的下面两个地址,找到chrome的快捷方式,右键属性 C:\Use ...
- Vuejs实例-02Vue.js项目集成ElementUI
Vuejs实例-02Vue.js项目集成ElementUI 0:前言 vue.js的UI组件库,在git上有多个项目,我见的使用者比较多的是iView和Element.两个组件库,组件都很丰富. 官网 ...
- 如何自学成为一个WEB前端
WEB前端是做什么的? 那些什么高大上的介绍作者就略过了,简单来说就是做网页的,我们上网浏览的网站界面就是WEB前端工程师做的. 在互联网迅速发展的近几年,你上网冲浪的时候是不是感觉WEB网站越来越漂 ...
- Calendar使用
1简单例子 package com.kungeek.tip; import java.text.SimpleDateFormat; import java.util.Calendar; import ...
- Eclipse之文件【默认编码格式设置】,防止乱码等问题
文件默认编码格式设置步骤如下: 这里显示的是workspace的视图 其他格式文件的视图如下:
- SqlDataReader 之指定转换无效
//获取最新显示顺序数据 string str = string.Format(@"if exists(select ShowOrder from GIS_FuncDefaultLayer ...
- Day2 - Python基础2习题集
1.购物车程序 product_list = [ (), (), (), (), (), (), ] shooping_list = [] salary = input("Input you ...
- JavaScript 特效三大系列总结
一. offset系列 1. offset系列的5个属性 1. offsetLeft : 用于获取元素到最近的定位父盒子的左侧距离 * 计算方式: 当前元素的左边框的左侧到定位父盒子的左边框右侧 * ...