关于配置文件的目录:[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[];
var yParts = y?.Split(Separator) ?? new string[]; // Compare each part until we get two parts that are not equal
for (int i = ; i < Math.Min(xParts.Length, yParts.Length); i++)
{
x = xParts[i];
y = yParts[i]; var value1 = ;
var value2 = ; var xIsInt = x != null && int.TryParse(x, out value1);
var yIsInt = y != null && int.TryParse(y, out value2); int result = ; 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 ? - : ;
} if (result != )
{
// 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
{
get
{
return _sources;
}
} public string BasePath
{
get;
} public IConfigurationBuilder Add(IConfigurationSource configurationSource)
{
return Add(configurationSource, load: true);
} public IConfigurationBuilder Add(IConfigurationSource configurationSource, bool load)
{
if (load)
{
configurationSource.Load();
}
_sources.Add(configurationSource);
return this;
} public IConfiguration Build()
{
return new ConfigurationSection(_sources);
}
}

ConfigurationBuilder

 public static class ConfigurationHelper
{
public static string ResolveConfigurationFilePath(IConfigurationBuilder configuration, string path)
{
if (!Path.IsPathRooted(path))
{
if (configuration.BasePath == null)
{
throw new InvalidOperationException(Resources.FormatError_MissingBasePath(
path,
typeof(IConfigurationBuilder).Name,
nameof(configuration.BasePath)));
}
else
{
path = Path.Combine(configuration.BasePath, path);
}
} return path;
}
}

ConfigurationHelper

ConfigurationSource和MemoryConfigurationSource

ConfigurationSource实现了IConfigurationSource接口,是其他具体的IConfigurationSource父类,该类是抽象类,不能直接实例化。

该类主要提供以下几个功能:

  • 用字典表保存key,value;并且提供get/set方法
  • 提供load方法(该类中是空的虚方法)
  • 给定制定前缀,获取该前缀下的子key(如:对于key值包含如下{“p1”,“p1:p2”,“p1:p3:p4”,“s1”},则通过“p1”可以获取到p2、p3)

MemoryConfigurationSource类继承自ConfigurationSource,提供了额外的方法:获取整个字典表。

[ConfigurationSource是扩展配置文件类型的基类,系统中就是通过继承自该类,实现xml以及json格式的配置文件类型]

public abstract class ConfigurationSource : IConfigurationSource
{
protected ConfigurationSource()
{
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
} protected IDictionary<string, string> Data { get; set; } public virtual bool TryGet(string key, out string value)
{
return Data.TryGetValue(key, out value);
} public virtual void Set(string key, string value)
{
Data[key] = value;
} public virtual void Load()
{
} public virtual IEnumerable<string> ProduceConfigurationSections(
IEnumerable<string> earlierKeys,
string prefix,
string delimiter)
{
return Data
.Where(kv => kv.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
.Select(kv => Segment(kv.Key, prefix, delimiter))
.Concat(earlierKeys)
.OrderBy(k => k, ConfigurationKeyComparer.Instance);
} private static string Segment(string key, string prefix, string delimiter)
{
var indexOf = key.IndexOf(delimiter, prefix.Length, StringComparison.OrdinalIgnoreCase);
return indexOf < ? key.Substring(prefix.Length) : key.Substring(prefix.Length, indexOf - prefix.Length);
}
}

ConfigurationSource

public class MemoryConfigurationSource :
ConfigurationSource,
IEnumerable<KeyValuePair<string,string>>
{
public MemoryConfigurationSource()
{
} public MemoryConfigurationSource(IEnumerable<KeyValuePair<string, string>> initialData)
{
foreach (var pair in initialData)
{
Data.Add(pair.Key, pair.Value);
}
} public void Add(string key, string value)
{
Data.Add(key, value);
} public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return Data.GetEnumerator();
} IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

MemoryConfigurationSource

ConfigurationSection和ConfigurationFocus

这两个类是双生类,使用了代理模式:通过GetConfigurationSection等获取IConfiguration方法返回的是ConfigurationFocus代理的ConfigurationSection(ConfigurationBuilder创建的是ConfigurationSection类)。简单的类关系图如下所示:(ConfigurationFocus不会和ConfigurationSource产生关联,会通过ConfigurationSection进行访问)

ConfigurationSection类实现的主要功能:

  • 根据key值获取配置信息(包含key值的IConfigurationSource中,ConfigurationBuilder最后添加的IConfigurationSource对象的key值所对应的value值)
  • 根据key值设置配置信息(所有IConfigurationSource文件都会被更新,最后信息不是保存在ConfigurationSection中,而是直接反应在IConfigurationSource上)
  • 重新加载配置源(IConfigurationSource)
  • 根据key值(可以为空)获取<string, IConfiguration>对应的字典表。(系统构建的IConfiguration就是ConfigurationFocus类型)

ConfigurationFocus实现的主要功能:

  • 内部封装ConfigurationSection对象
  • 内部封装当前的前缀信息
  • 根据内部封装的前缀信息+key构造新的key值,之后通过ConfigurationSection获取新key值配置信息/设置新key配置信息
  • 根据内部封装的前缀信息+key构造新的key值,之后通过ConfigurationSection获取新key配置信息。(当前配置信息下一级为key的配置信息)
  • 根据内部封装的前缀信息+key构造新的key值,之后通过ConfigurationSection获取子<string, IConfiguration>对应的字典表。(当先配置信息下一级为key的配置信息的所有子配置信息)
public class ConfigurationSection : IConfiguration
{
private readonly IList<IConfigurationSource> _sources = new List<IConfigurationSource>(); public ConfigurationSection(IList<IConfigurationSource> sources)
{
_sources = sources;
} public string this[string key]
{
get
{
return Get(key);
}
set
{
Set(key, value);
}
} public IEnumerable<IConfigurationSource> Sources
{
get
{
return _sources;
}
} public string Get([NotNull] string key)
{
string value;
return TryGet(key, out value) ? value : null;
} public bool TryGet([NotNull] string key, out string value)
{
// If a key in the newly added configuration source is identical to a key in a
// formerly added configuration source, the new one overrides the former one.
// So we search in reverse order, starting with latest configuration source.
foreach (var src in _sources.Reverse())
{
if (src.TryGet(key, out value))
{
return true;
}
}
value = null;
return false;
} public void Set([NotNull] string key, [NotNull] string value)
{
if (!_sources.Any())
{
throw new InvalidOperationException(Resources.Error_NoSources);
}
foreach (var src in _sources)
{
src.Set(key, value);
}
} public void Reload()
{
foreach (var src in _sources)
{
src.Load();
}
} public IConfiguration GetConfigurationSection(string key)
{
return new ConfigurationFocus(this, key + Constants.KeyDelimiter);
} public IEnumerable<KeyValuePair<string, IConfiguration>> GetConfigurationSections()
{
return GetConfigurationSectionsImplementation(string.Empty);
} public IEnumerable<KeyValuePair<string, IConfiguration>> GetConfigurationSections([NotNull] string key)
{
return GetConfigurationSectionsImplementation(key + Constants.KeyDelimiter);
} private IEnumerable<KeyValuePair<string, IConfiguration>> GetConfigurationSectionsImplementation(string prefix)
{
var segments = _sources.Aggregate(
Enumerable.Empty<string>(),
(seed, source) => source.ProduceConfigurationSections(seed, prefix, Constants.KeyDelimiter)); var distinctSegments = segments.Distinct(); return distinctSegments.Select(segment => CreateConfigurationFocus(prefix, segment));
} private KeyValuePair<string, IConfiguration> CreateConfigurationFocus(string prefix, string segment)
{
return new KeyValuePair<string, IConfiguration>(
segment,
new ConfigurationFocus(this, prefix + segment + Constants.KeyDelimiter));
}
}

ConfigurationSection

public class ConfigurationFocus : IConfiguration
{
private readonly string _prefix;
private readonly IConfiguration _root; public ConfigurationFocus(IConfiguration root, string prefix)
{
_prefix = prefix;
_root = root;
} public string this[string key]
{
get
{
return Get(key);
}
set
{
Set(key, value);
}
} public string Get(string key)
{
// Null key indicates that the prefix passed to ctor should be used as a key
if (key == null)
{
// Strip off the trailing colon to get a valid key
var defaultKey = _prefix.Substring(, _prefix.Length - );
return _root.Get(defaultKey);
} return _root.Get(_prefix + key);
} public bool TryGet(string key, out string value)
{
// Null key indicates that the prefix passed to ctor should be used as a key
if (key == null)
{
// Strip off the trailing colon to get a valid key
var defaultKey = _prefix.Substring(, _prefix.Length - );
return _root.TryGet(defaultKey, out value);
}
return _root.TryGet(_prefix + key, out value);
} public IConfiguration GetConfigurationSection(string key)
{
return _root.GetConfigurationSection(_prefix + key);
} public void Set(string key, string value)
{
_root.Set(_prefix + key, value);
} public IEnumerable<KeyValuePair<string, IConfiguration>> GetConfigurationSections()
{
return _root.GetConfigurationSections(_prefix.Substring(, _prefix.Length - ));
} public IEnumerable<KeyValuePair<string, IConfiguration>> GetConfigurationSections(string key)
{
return _root.GetConfigurationSections(_prefix + key);
} public void Reload()
{
throw new InvalidOperationException(Resources.Error_InvalidReload);
}
}

ConfigurationFocus

最后我们将ConfigurationSection和ConfigurationFocus的测试代码贴出,以便能够更好的理解俩个类的关系。

public void CanGetConfigurationSection()
{
// Arrange
var dic1 = new Dictionary<string, string>()
{
{"Data:DB1:Connection1", "MemVal1"},
{"Data:DB1:Connection2", "MemVal2"}
};
var dic2 = new Dictionary<string, string>()
{
{"DataSource:DB2:Connection", "MemVal3"}
};
var dic3 = new Dictionary<string, string>()
{
{"Data", "MemVal4"}
};
var memConfigSrc1 = new MemoryConfigurationSource(dic1);
var memConfigSrc2 = new MemoryConfigurationSource(dic2);
var memConfigSrc3 = new MemoryConfigurationSource(dic3); var builder = new ConfigurationBuilder();
builder.Add(memConfigSrc1, load: false);
builder.Add(memConfigSrc2, load: false);
builder.Add(memConfigSrc3, load: false); var config = builder.Build(); string memVal1, memVal2, memVal3, memVal4, memVal5;
bool memRet1, memRet2, memRet3, memRet4, memRet5; // Act
var configFocus = config.GetConfigurationSection("Data"); memRet1 = configFocus.TryGet("DB1:Connection1", out memVal1);
memRet2 = configFocus.TryGet("DB1:Connection2", out memVal2);
memRet3 = configFocus.TryGet("DB2:Connection", out memVal3);
memRet4 = configFocus.TryGet("Source:DB2:Connection", out memVal4);
memRet5 = configFocus.TryGet(null, out memVal5); // Assert
Assert.True(memRet1);
Assert.True(memRet2);
Assert.False(memRet3);
Assert.False(memRet4);
Assert.True(memRet5); Assert.Equal("MemVal1", memVal1);
Assert.Equal("MemVal2", memVal2);
Assert.Equal("MemVal4", memVal5); Assert.Equal("MemVal1", configFocus.Get("DB1:Connection1"));
Assert.Equal("MemVal2", configFocus.Get("DB1:Connection2"));
Assert.Null(configFocus.Get("DB2:Connection"));
Assert.Null(configFocus.Get("Source:DB2:Connection"));
Assert.Equal("MemVal4", configFocus.Get(null)); Assert.Equal("MemVal1", configFocus["DB1:Connection1"]);
Assert.Equal("MemVal2", configFocus["DB1:Connection2"]);
Assert.Null(configFocus["DB2:Connection"]);
Assert.Null(configFocus["Source:DB2:Connection"]);
Assert.Equal("MemVal4", configFocus[null]);
}

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

  1. [Asp.net 5] Configuration-新一代的配置文件

    微软新一代asp.net(vnext),也叫asp.net 5,开源代码都放在网址https://github.com/aspnet下. 本文介绍的是Configuration工程,下载路径为http ...

  2. C#/ASP.NET应用程序配置文件app.config/web.config的增、删、改操作,无法为请求的 Configuration 对象创建配置文件。

    应用程序配置文件,对于asp.net是 web.config,对于WINFORM程序是 App.Config(ExeName.exe.config). 配置文件,对于程序本身来说,就是基础和依据,其本 ...

  3. [Asp.net 5] Configuration-新一代的配置文件(ConfigurationSource的多种实现)

    关于配置文件的目录:[Asp.net 5] Configuration-新一代的配置文件 在前面我们介绍了,系统中用IConfigurationSource表示不同配置文件的来源,起到读取.设置.加载 ...

  4. [Asp.net 5] Configuration-新一代的配置文件(神奇的Binder)

    关于配置文件的目录:[Asp.net 5] Configuration-新一代的配置文件 之前看过MVC4.0的源码,里面就有Binder.作用是将前台页面传递过来的键值对/字典表绑定到特定的对象.此 ...

  5. Asp.net Core 和类库读取配置文件信息

    Asp.net Core 和类库读取配置文件信息 看干货请移步至.net core 读取配置文件公共类 首先开一个脑洞,Asp.net core 被使用这么长时间了,但是关于配置文件(json)的读取 ...

  6. 无法为请求的 Configuration 对象创建配置文件 错误原因

    Configuration config = WebConfigurationManager.OpenWebConfiguration("~"); 无法为请求的 Configura ...

  7. ASP.NET Web API 入门 (API接口、寄宿方式、HttpClient调用)

    一.ASP.NET Web API接口定义 ASP.NET Web API默认实现了Action方法和HTTP方法的映射,Action方法方法名体现了其能处理的请求必须采用的HTTP方法 二.寄宿方式 ...

  8. ASP.NET WEB API微信支付通知接口,返回xml数据,微信服务器不识别问题

    原文:ASP.NET WEB API微信支付通知接口,返回xml数据,微信服务器不识别问题 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/MrTra ...

  9. ASP.NET MVC 下使用支付宝支付接口 以及 ASP.NET Core 下相关改造支付

    通过nuget首先引用AopSdk.dll 包 下面写的是 Asp.Net MVC 下相关的支付接口 APP支付 配置客户端相关的参数,配置成自己的代码就可以了 private string APPI ...

随机推荐

  1. 渣渣小本求职复习之路每天一博客系列——TCP/IP协议栈(5)

    前情回顾:一篇短短的博客明显不能满足TCP和UDP这两个饥渴的汉子,而且还被应用协议占了一小半的篇幅.在昨天结束之后,相信大家都基本对TCP/IP协议栈的轮廓有一个大概的印象了,能够对整体有所把握. ...

  2. [蓝牙] 6、基于nRF51822的蓝牙心率计工程消息流Log分析(详细)

    开机初始化Log Log编号 函数名   所在文件名 000001: main ..\main.c 000002: timers_init ..\main.c 000003: gpiote_init ...

  3. [stm32] STM32 Interrupts and events 系统了解(EXTI)及槽型光电开关tp850电路研究

    中断和事件 1 嵌套向量中断控制器 特性: ● 68个可屏蔽中断通道(不包含16个Cortex™-M3的中断线):● 16个可编程的优先等级(使用了4位中断优先级):● 低延迟的异常和中断处理:● 电 ...

  4. Spring-Context之三:使用XML和Groovy DSL配置Bean

    在第一讲中显示了如何使用注解配置bean,其实这是Spring3引进的特性,Spring2使用的是XML的方式来配置Bean,那时候漫天的XML文件使得Spring有着配置地狱的称号.Spring也一 ...

  5. 浅析SQL Server实现分布式事务的两阶段提交协议2PC

    不久之前团队有个新人问我一个很重要的web服务接口如何保证事务的问题.因为涉及到跨库事务,当时我只是回答目前我们的SOA框架都不支持跨库事务.然后就问到了数据库跨库事务是如何实现的,我只能凭印象含糊回 ...

  6. ios 常用数学函数

    需要 引入头文件 #import <math.h> 1. 三角函数  double sin (double);正弦  double cos (double);余弦  double tan ...

  7. Atitit 代理CGLIB 动态代理 AspectJ静态代理区别

    Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...

  8. CGContextTranslateCTM: invalid context 0x0. If you want to see the backtrace, please set CG_CONTEXT_SHOW_BACKTRACE environmental variable.

    最近在测试的过程中, 发现了SpringBoar的一个问题: SpringBoard[53] <Error>: CGContextTranslateCTM: invalid context ...

  9. 【WP开发】正确理解页面缓存

    注:本文内容面向Runtime App. 在新建项目后,细心观察,你会发现在App类中有以下代码: // TODO: 将此值更改为适合您的应用程序的缓存大小 rootFrame.CacheSize = ...

  10. 基于Metronic的Bootstrap开发框架经验总结(9)--实现Web页面内容的打印预览和保存操作

    在前面介绍了很多篇相关的<Bootstrap开发框架>的系列文章,这些内容基本上覆盖到了我这个Bootstrap框架的各个主要方面的内容,总体来说基本达到了一个稳定的状态,随着时间的推移可 ...