前言
之前接触到Redis,然后选用了对StackExchange.Redis又一层封装的StackExchange.Redis.Extensions.Core类库。阅读源代码的过程中发现了他使用Configuration实现读取自定义配置的方法。特此学习并记录。在我们日常开发中,最常用的自定义配置读取方式莫过于如下两种方式,尤其是连接数据库。
//读取appsetting var appSettingValue = ConfigurationManager.AppSettings["KEY"]; //读取connectionstring var connectionValue = ConfigurationManager.ConnectionStrings["KEY"];
而在使用这个类库的时候它的配置形式是这样的:
<configSections> <section name="redisCacheClient" type="StackExchange.Redis.Extensions.Core.Configuration.RedisCachingSectionHandler, StackExchange.Redis.Extensions.Core" /> </configSections> <redisCacheClient allowAdmin="true" ssl="false" connectTimeout="5000" database="0" password=""> <hosts> <add host="127.0.0.1" cachePort="6379"/> <add host="127.0.0.1" cachePort="6380"/> </hosts> </redisCacheClient>
没错,就是自定义section,然后在读取section中详细的配置信息,如上述代码所示,sectionName=redisCacheClient,allowAdmin,ssl等都是它的配置信息。<hosts>节点比较特殊,它是一系列配置信息的集合。
代码解读
先看一下自定义配置接口,里面就包含了 redisCacheClient中的各种属性定义
/// <summary> /// 自定义配置接口 /// </summary> public interface IRedisCachingConfiguration { /// <summary> /// Redis Server的服务端口配置 /// </summary> /// <value> /// IP地址或者服务器名称 /// </value> RedisHostCollection RedisHosts { get; } /// <summary> /// The strategy to use when executing server wide commands /// </summary> ServerEnumerationStrategy ServerEnumerationStrategy { get; } /// <summary> /// 定义是否该连接可以使用管理员权限操作,比如flush database /// </summary> /// <value> /// <c>true</c> 可以使用管理员权限; 否则, <c>false</c>. /// </value> bool AllowAdmin { get; } /// <summary> /// 是否SSL安全加密 /// </summary> /// <value> /// <c>true</c> if is secure; otherwise, <c>false</c>. /// </value> bool Ssl { get; } /// <summary> /// 连接超时时间 /// </summary> int ConnectTimeout { get; } /// <summary> /// 没有服务可用的时候,不会创建新连接 /// </summary> bool AbortOnConnectFail { get; } /// <summary> /// Database Id /// </summary> /// <value> /// database的ID,默认为0 /// </value> int Database { get; } /// <summary> /// 密码 /// </summary> string Password { get; } }
我们看一下类的具体实现,首先要继承自定义的接口,还要继承ConfigurationSection,这样当我们取比如说 allowAdmin的值的时候可以在内部直接调用 this["allowAdmin"]取到值了。
/// <summary> /// 继承自定义接口,并且继承ConfigurationSection<see cref="IRedisCachingConfiguration"/> /// </summary> public class RedisCachingSectionHandler : ConfigurationSection, IRedisCachingConfiguration { //这里就只拿allowAdmin举例,默认是string类型,那么我们就要根据自己的需求进行数据类型转换了。这里就把string类型转换为我们想要的bool类型 [ConfigurationProperty("allowAdmin")] public bool AllowAdmin { get { bool result = false; var config = this["allowAdmin"]; if (config != null) { var value = config.ToString(); if (!string.IsNullOrEmpty(value)) { if (bool.TryParse(value, out result)) { return result; } } } return result; } } //其他代码 ... .../// <summary> /// 读取配置信息,外部调用主方法 /// </summary> /// <returns></returns> public static RedisCachingSectionHandler GetConfig() { return ConfigurationManager.GetSection("redisCacheClient") as RedisCachingSectionHandler; } }
下面我们在看一下元素集合的使用,单节点RedisHost代码如下:
/// <summary> /// RedisHost的配置元素 /// </summary> public class RedisHost : ConfigurationElement { /// <summary> /// Gets the Redis host. /// </summary> /// <value> ///获取host节点值 /// </value> [ConfigurationProperty("host", IsRequired = true)] public string Host { get { return this["host"] as string; } } /// <summary> /// Gets the port. /// </summary> /// <value> /// 获取cachePort的值 /// </value> [ConfigurationProperty("cachePort", IsRequired = true)] public int CachePort { get { var config = this["cachePort"]; if (config != null) { var value = config.ToString(); if (!string.IsNullOrEmpty(value)) { int result; if (int.TryParse(value, out result)) { return result; } } } throw new Exception("Redis Cahe port must be number."); } } }
还需要定义一个Collection将Hosts中的内容收集起来。类似List
/// <summary> /// Configuration Element Collection for <see cref="RedisHost"/> /// </summary> public class RedisHostCollection : ConfigurationElementCollection { /// <summary> /// Gets or sets the <see cref="RedisHost"/> at the specified index. /// </summary> /// <value> /// The <see cref="RedisHost"/>. /// </value> /// <param name="index">The index.</param> /// <returns></returns> public RedisHost this[int index] { //BaseGet,BaseRemoveAt,BaseAdd都是 ConfigurationElementCollection 中定义的方法 get { //调用BaseGet方法获取节点信息 return BaseGet(index) as RedisHost; } set { //设置的时候先删掉,在添加 if (BaseGet(index) != null) { BaseRemoveAt(index); } BaseAdd(index, value); } } /// <summary> ///此方法需要重写,返回一个新节点 /// </summary> /// <returns></returns> protected override ConfigurationElement CreateNewElement() { return new RedisHost(); } /// <summary> /// 重写此方法,获取元素key /// </summary> /// <param name="element">元素</param> /// <returns></returns> protected override object GetElementKey(ConfigurationElement element) //这里可以看到,这个key就是 Host:Port => $"{((RedisHost) element).Host}:{((RedisHost) element).CachePort}"; }
经过一层层的包装之后,Handler中后去Host的节点方法就很简单了。
/// <summary> /// The host of Redis Server /// </summary> /// <value> /// The ip or name /// </value> [ConfigurationProperty("hosts")] public RedisHostCollection RedisHosts => this["hosts"] as RedisHostCollection;
我们运行程序读取一下试试:
RedisCachingSectionHandler config = RedisCachingSectionHandler.GetConfig(); Console.WriteLine("config中的allowAdmin:" + config.AllowAdmin); Console.WriteLine("config中的ssl:" + config.Ssl); Console.WriteLine("config中的password:" + config.Password); Console.WriteLine("config中的database:" + config.Database); Console.WriteLine(); Console.WriteLine("读取Host信息如下:"); foreach (RedisHost host in config.RedisHosts) { Console.WriteLine($"{host.Host}:{host.CachePort}"); } Console.Read();
运行结果:
config中的信息已经正常读取到。那么我们可以用这种方式实现读取自己自定义的配置信息啦。当然,简单的配置还是直接用 <add key="key" value="value"/>
总结
微软库已经给我们提供了太多的方法,在不知情的情况下我们往往会自己去实现。多看看开源代码对自己知识的提升还有有帮助的。这不,学会了这种配置方法,我们就可使用不仅仅ConfigurationManager这个类了。