·您现在的位置: 云翼网络 >> 文章中心 >> 网站建设 >> 网站建设开发 >> ASP.NET网站开发 >> [Asp.net 5] Configuration-新一代的配置文件(接口定义与基础实现)

[Asp.net 5] Configuration-新一代的配置文件(接口定义与基础实现)

作者:佚名      ASP.NET网站开发编辑:admin      更新时间:2022-07-23

[asp.net 5] Configuration-新一代的配置文件(接口定义与基础实现)

关于配置文件的目录:[Asp.net 5] Configuration-新一代的配置文件

本系列文章讲的是asp.net 5(Asp.net VNext)中的配置文件部分,工程下载地址为:https://github.com/aspnet/Configuration

本节讲的是Configuration解决方案中的Microsoft.Framework.Configuration和Microsoft.Framework.Configuration.Abstractions俩个工程。

Abstractions

首先我们看下Configuration.Abstractions这个工程的详情:

该工程中只定义了三个接口:IConfiguration、IConfigurationBuilder、IConfigurationSource,是完全为了抽象而设计的工程。

我们在依赖注入(DependencyInjection)篇中也接触过名字为“Abstractions”的工程(链接地址:http://www.cnblogs.com/watermoon2/p/4511269.html),也是只包含必须的接口定义,我们可以推测,微软的命名规则是对于XXXX类工程:

  • Microsoft.Framework.XXXX.Abstractions:定义微软XXXX的必须的抽象
  • Microsoft.Framework.XXXX:定义微软的XXXX的基础实现,内部类多实现Microsoft.Framework.XXXX.Abstractions中接口

配置文件中,肯定少不了配置文件类的基础接口定义:IConfiguration;我们知道新的配置文件实现,支持配置文件有多个来源,可以来自xml、可以来自json、也可以既有部分来自xml,又有部分来自json,所以接口中定义了“IConfigurationSource”接口,用于标示配置文件的来源;而IConfigurationBuilder是IConfiguration的构造器。

这个工程代码比较少,下面我就将接口定义罗列如下:

public interface IConfigurationSource    {        bool TryGet(string key, out string value);        void Set(string key, string value);        void Load();        IEnumerable<string> PRoduceConfigurationSections(            IEnumerable<string> earlierKeys,            string prefix,            string delimiter);    } public interface IConfigurationBuilder    {        string BasePath { get; }        IEnumerable<IConfigurationSource> Sources { get; }        IConfigurationBuilder Add(IConfigurationSource configurationSource);        IConfiguration Build();    }public interface IConfiguration    {        string this[string key] { get; set; }        string Get(string key);        bool TryGet(string key, out string value);        IConfiguration GetConfigurationSection(string key);        IEnumerable<KeyValuePair<string, IConfiguration>> GetConfigurationSections();        IEnumerable<KeyValuePair<string, IConfiguration>> GetConfigurationSections(string key);        void Set(string key, string value);        void Reload();    }
接口定义

Configuration

我们还是将工程的详情列出:

工程中一共八个cs文件:

1,IConfigurationSource实现类:ConfigurationSource、MemoryConfigurationSource

2,IConfigurationBuilder实现类:ConfigurationBuilder;IConfigurationBuilder扩展方法:ConfigurationHelper

3,IConfiguration实现类:ConfigurationSection、ConfigurationFocus

4,帮助辅助类:ConfigurationKeyComparer、Constants。

一个约定:":"

我们知道配置文件不都是线性的,可能有层次结构(比如传统的配置文件、json的、xml的)。我们读取配置文件的key值就需要有一定的逻辑。现在的逻辑是:

  • 根节点对象:“当前key”
  • 非根节点对象:“前缀”+“分隔符”+“当前key"(前缀是当前节点父节点的key值)

所以对于如下的json格式{"root1":"r1","root2":{"sub1":"s2"}},想要获取值是“s2”,所使用的key值是“root2:sub1”;“root2”是父节点的key,“:”是分隔符,“sub1”是当前key。

在这里的分隔符,其实就是定义在Constants类中,public static readonly string KeyDelimiter = ":"; 不过源文件中其他部分并未都直接使用该处定义,在IConfigurationSource的派生类也都是自己定义的“:”;所以想修改分隔符,在现有代码中不是能够只修改Constants中这个全局变量就可以的。所以在源码还有问题的时候,我们还是把分隔符=“:”,作为一个约定(不要试图把分隔符该城其他字符串)。

特殊的排序方式

由于当前key值得字符串可能是由存数字组成,我们希望key值为“1”,“2”,“10”的顺序是“1”,“2”,“10” 而不是“1”,“10”,“2”(字符串默认排序的顺序),所以系统在排序的时候使用了IComparer<string>接口。而IComparer<string>接口的实现类就是ConfigurationKeyComparer

public class ConfigurationKeyComparer : IComparer<string>    {        private const char Separator = ':';        public static ConfigurationKeyComparer Instance { get; } = new ConfigurationKeyComparer();        public int Compare(string x, string y)        {            var xParts = x?.Split(Separator) ?? new string[0];            var yParts = y?.Split(Separator) ?? new string[0];            // Compare each part until we get two parts that are not equal            for (int i = 0; i < Math.Min(xParts.Length, yParts.Length); i++)            {                x = xParts[i];                y = yParts[i];                var value1 = 0;                var value2 = 0;                var xIsInt = x != null && int.TryParse(x, out value1);                var yIsInt = y != null && int.TryParse(y, out value2);                int result = 0;                if (!xIsInt && !yIsInt)                {                    // Both are strings                    result = string.Compare(x, y, StringComparison.OrdinalIgnoreCase);                }                else if (xIsInt && yIsInt)                {                    // Both are int                     result = value1 - value2;                }                else                {                    // Only one of them is int                    result = xIsInt ? -1 : 1;                }                if (result != 0)                {                    // One of them is different                    return result;                }            }            // If we get here, the common parts are equal.            // If they are of the same length, then they are totally identical            return xParts.Length - yParts.Length;        }    }
ConfigurationKeyComparer

前面的铺垫已经讲完,下面我们进入正文:ConfigurationBuilder以及ConfigurationHelper

ConfigurationBuilder的功能主要有四点:

  • 能够设置加载的IConfigurationSource源路径目录
  • 能够管理的IConfigurationSource列表
  • 能够加载IConfigurationSource
  • 能够创建IConfiguration

代码中需要注意的也就只有一点:添加新的IConfigurationSource时,首先加载,之后再将IConfigurationSource对象添加到内部IConfigurationSource列表中。

ConfigurationHelper是ConfigurationBuilder的扩展,作用只有一个:

  • 将如果传入路径是相对路径,将IConfigurationSource源路径目录和传入路径进行合并。

ConfigurationBuilder以及ConfigurationHelper源码如下:

public class ConfigurationBuilder : IConfigurationBuilder    {        private readonly IList<IConfigurationSource> _sources = new List<IConfigurationSource>();        public ConfigurationBuilder(params IConfigurationSource[] sources)            : this(null, sources)        {        }        public ConfigurationBuilder(string basePath, params IConfigurationSource[] sources)        {            if (sources != null)            {                foreach (var singleSource in sources)                {                    Add(singleSource);                }            }            BasePath = basePath;        }        public IEnumerable<IConfigurationSource> Sources        {            ge