前言
之前接觸到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這個類了。