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

摘要:
在《实现自己的.NETCore配置Provider之EF》中已经讲过配置的执行流程,这里不再复述,直接动手。YamlConfigurationProviderYaml是基于文件的,可以直接从FileConfigurationProvider继承,在FileConfigurationProvider实现了监控文件变化并自动重新加载的功能。internalclassYamlConfigurationProvider:FileConfigurationProvider{publicYamlConfigurationProvider:base{}publicoverridevoidLoad{varparser=newYamlConfigurationFileParser();Data=parser.Parse;}}YamlConfigurationParser是解析Yaml文件的核心,后面会介绍。YamlConfigurationSourceinternalclassYamlConfigurationSource:FileConfigurationSource{publicoverrideIConfigurationProviderBuild{EnsureDefaults;returnnewYamlConfigurationProvider;}}YamlConfigurationSource实现父类的Build方法,返回YamlConfigurationProvider。AddYamlFile扩展方法为添加Yaml配置源增加扩展方法。publicstaticclassYamlConfigurationExtensions{publicstaticIConfigurationBuilderAddYamlFile{returnAddYamlFile;}publicstaticIConfigurationBuilderAddYamlFile{returnAddYamlFile;}publicstaticIConfigurationBuilderAddYamlFile{returnAddYamlFile;}publicstaticIConfigurationBuilderAddYamlFile{if{thrownewArgumentNullException;}if{thrownewArgumentException;}returnbuilder.AddYamlFile;}internalstaticIConfigurationBuilderAddYamlFile{varsource=newYamlConfigurationSource();configureSource;returnbuilder.Add;}}YamlConfigurationFileParser解析Yaml是核心的功能,目前github有开源的C#Yaml项目:YamlDotNet和SharpYaml。SharpYamlFork自YamlDotNet,但做了不少改进并支持Yaml1.2,不过需要netstandard1.6+。关于Yaml可以参考阮一峰老师的《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项目: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之Yaml》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【原创】重绘winform的GroupBoxkubelet全部参数整理下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

NodeJS + Socket.IO 最终版

服务器端 //socket.io var app = require("express")(); var http = require("http").Server(app); var io = require("socket.io")(http); //couchbase var couchbase = require("couchbase"); va...

容器编排系统K8s之包管理器Helm基础使用(一)

前文我们了解了k8s上的hpa资源的使用,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14293237.html;今天我们来聊一下k8s包管理器helm的相关话题; helm是什么? 如果我们把k8s的资源清单类比成centos上的rpm包,那么helm的作用就如同yum;简单讲helm就是类似yum这样的包管理...

【Android】是时候为你的应用加上WebDav同步了

WebDav是什么? WebDAV (Web-based Distributed Authoring and Versioning) 一种基于HTTP1.1协议的通信协议。它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,使应用程序可对Web Server直接读写,并支持写文件锁定(Locking)及解锁(...

Java安全之Weblogic内存马

Java安全之Weblogic内存马 0x00 前言 发现网上大部分大部分weblogic工具都是基于RMI绑定实例回显,但这种方式有个弊端,在Weblogic JNDI树里面能将打入的RMI后门查看得一清二楚。并且这种方式实现上传Webshell落地文件容易被Hids监测。 0x01 调试分析 调试分析 写一个filter进行断点跟踪上层代码。 其实和T...

三、Electron + Webpack + Vue 搭建开发环境及打包安装 ---- 打包electron应用

目录 Webpack + Vue 搭建开发环境及打包安装 ------- 打包渲染进程 Electron + Webpack  搭建开发环境及打包安装 ------- 打包主进程 Electron + Webpack + Vue 搭建开发环境及打包安装 ---- 打包electron应用 三、打包Election App 应用   在之前的节中已经写...

Delphi的类与继承

     既然已经做出了com程序用delphi来开发的决定,那当然就要对delphi进行一些深入的了解。有人说delphi是一个用控件堆砌起来的工具,和vb没什么两样;也有人说dephi实际上是面向过程的,他的面向对象并不彻底。实际生活中持这两种观点的人不在少数,就拿我认识的一个非常好的程序员来说吧,他很早就开始用vb,到后来接触到delphi,并且用d...