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项目:YamlDotNetSharpYaml 。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的更多相关文章

  1. 实现自己的.NET Core配置Provider之EF

    <10分钟就能学会.NET Core配置>里详细介绍了.NET Core配置的用法,另外我还开源了自定义的配置Provider:EF配置Provider和Yaml配置Provider.本文 ...

  2. 10分钟就能学会的.NET Core配置

    .NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列化为PO ...

  3. 【转】10分钟就能学会的.NET Core配置

    .NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列化为PO ...

  4. SQLite EF Core Database Provider

    原文链接 This database provider allows Entity Framework Core to be used with SQLite. The provider is mai ...

  5. Net core学习系列(九)——Net Core配置

    一.简介 NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列 ...

  6. 关于Asp.net core配置信息读取的源码分析梳理

    概述 我们都知道asp.net core配置信息的读取离不开IConfigurationSource和IConfigurationProvider这两个类,ConfigurationSource可以提 ...

  7. ASP.NET Core配置Kestrel 网址Urls

    ASP.NET Core中如何配置Kestrel Urls呢,大家可能都知道使用UseUrls() 方法来配置. 今天给介绍全面的ASP.NET Core 配置 Urls,使用多种方式配置Urls.让 ...

  8. net core体系-web应用程序-4net core2.0大白话带你入门-4asp.net core配置项目访问地址

    asp.net core配置访问地址  .net core web程序,默认使用kestrel作为web服务器. 配置Kestrel Urls有四种方式,我这里只介绍一种.其它方式可自行百度. 在Pr ...

  9. .net core 配置

    .net core 配置包括很多种 例如内存变量.命令行参数.环境变量以及物理文件配置和自定义配置 物理文件配置主要有三种,它们分别是JSON.XML和INI,对应的配置源类型分别是JsonConfi ...

随机推荐

  1. 使用MyEclipse生成实体类和Hibernate映射文件

    1.打开MyEclipse DataBase Explorer. 如上图,点击图中2便可切换到MyEclipse DataBase Explorer,若没有该图标,则点击图中1,便可选择MyEclip ...

  2. 我对Stub和Mock的理解

    介绍 使用测试驱动开发大半年了,我还是对Stub和Mock的认识比较模糊,没有进行系统整理. 今天查阅了相关资料,觉得写得很不错,所以我试图在博文中对资料进行整理一下,再加上一些自己的观点. 本文是目 ...

  3. 关于Dubbo一个接口多个实现的解决方案

    如题,其实这个问题在官方文档中已经说明了.我直接贴图就好了 更多学习请参考:minglisoft.cn/technology

  4. winows 服务器环境搭建 (碰到了windows服务器,小记一下吧~)

    1.连接远程服务器  安装wamp 2.查看wamp 默认端口号是否与对应windows 服务器冲突,如果冲突,则改之 WAMP装好之后默认的端口是80,但是这个80端口呢,可以热门端口啊,迅雷,II ...

  5. Spark操作HBase问题:java.io.IOException: Non-increasing Bloom keys

    1 问题描述 在使用Spark BulkLoad数据到HBase时遇到以下问题: 17/05/19 14:47:26 WARN scheduler.TaskSetManager: Lost task ...

  6. 新鲜小玩意儿- deviceOrientation移动设备旋转事件

    <javascript高级程序设计>第三版 其中事件的章节 提到一个有意思的事件 deviceOrientation 也就是 设备(device) - orientation(方向) 贴代 ...

  7. javaWeb学习总结(4)- HttpServletResponse

    一.简介: Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象. request和response对象即然代表请求和 ...

  8. Java 8 新特性1-函数式接口

    Java 8 新特性1-函数式接口 (原) Lambda表达式基本结构: (param1,param2,param3) -> {代码块} 例1: package com.demo.jdk8; i ...

  9. mysqldump命令详解

    1.数据备份的重要性: 保护公司的数据 网站的7x24提供服务 2.MySQL数据库备份: --all-databases , -A 导出全部数据库. mysqldump -uroot -p --al ...

  10. 【小练习04】HTML+CSS--医药健康小页面

    要求实现如下效果图: 代码演示 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"&g ...