StackExchange.Redis.Extensions.Core 源码解读之 Configuration用法

前言

  之前接触到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这个类了。

原文地址:https://www.cnblogs.com/panzi/p/5833698.html