摘要
上一篇講述了安裝redis客戶端和服務器端,也大體地介紹了一下redis。本篇着重講解.NET4.0 和 .NET4.5中如何使用redis和C# redis操作哈希表。並且會將封裝的一些代碼貼一下。在講解的過程中,我打算結合redis操作命令一起敘述,算是作為對比吧。這樣也能讓讀者清楚了 解,所分裝的代碼對應的redis的哪一些操作命令。
hash哈希表簡介
這里僅僅是對哈希表作簡單概念級介紹(摘自csdn),如果需要,自己去研究。
1、哈希表的概念
哈希表(Hash Table)也叫散列表,是根據關鍵碼值(Key Value)而直接進行訪問的數據結構。它通過把關鍵碼值映射到哈希表中的一個位置來訪問記錄,以加快查找的速度。這個映射函數就做散列函數,存放記錄的數組叫做散列表。
2、哈希表查找的時間復雜度
哈希表存儲的是鍵值對,其查找的時間復雜度與元素數量多少無關,哈希表在查找元素時是通過計算哈希碼值來定位元素的位置從而直接訪問元素的,因此,哈希表查找的時間復雜度為O(1)。
如何在.NET4.0/4.5中安裝redis組件?
在上一篇博文中,安裝好的redis服務器端,要記得開啟服務。然后再在.NET4.5(.NET4.0同理)項目中添加對redis操作的dll文件的引用。引用的步驟如下:
第一步:右鍵項目中的引用,選擇“管理NuGet程序包”;
第二步:在搜索欄中輸入“Redis client for the Redis NoSQL DB”,聯機查找;如下圖:
聯機搜索結構中的第一個(如上圖紅色區域的組件,其版本號為4.0.35)就是要安裝的組件了。接下來我們就繼續點擊“安裝”按鈕,進入下載組件,等下載完成后,繼續選擇“接受條款”,然后繼續安裝。安裝過程中會出現下圖情況:
這句紅色錯誤的意思是我們安裝的ServiceStack.Interfaces 4.0.35版本與當前的.NET4.5框架中組件不兼容。這說明我們需要降低或是提高.NET版本解決此問題。我安裝的是.NET4.5,所以我只能降 低版本。降低.NET版本的方法大家應該都知道的。我就在累述一次,希望大家不要嫌煩,畢竟還有新手在。方法:右鍵項目文件選擇“屬性”==》“應用程 序”==》“目標框架”,然后選擇各個版本去嘗試之前的兩步操作,直到可以安裝為止。
我試過了,.NET4.0也會遇到同樣問題,直到.NET3.5才可以。當然此時安裝的ServiceStack.Interfaces 版本是1.0.0.0 ,這樣我們再把.NET版本恢復4.5即可使用了。,其實是變相在.NET4.0/4.5下使用redis客戶端。不知道各位有沒有遇到這樣的問題,還是 直接拷貝別人的dll文件。當自己親自去操作的時候,才會發現,其實就算是安裝一個組件都可能會出現各種各樣的問題。所以,要想了解全過程的話,我們還是 要身體力行的啊。好了,這樣就說明在.NET4.5下安裝好了redis client了。
實戰:在項目中運用redis代碼詳解
這部分主要是講解怎樣連接到redis服務器。其中包含很多配置,我就用代碼去說明一切了。配置文件如下代碼:
1 <configSections>
2 <section name="RedisConfig" type="RedisDemo.Common.Redis.RedisConfigInfo, RedisDemo"/> 3 </configSections> 4 <RedisConfig WriteServerList="127.0.0.1:6379" ReadServerList="127.0.0.1:6379" MaxWritePoolSize="60"
MaxReadPoolSize="60" AutoStart="true" LocalCacheTime="180" RecordeLog="false"/>
在這里對RedisConfig這段配置文件的屬性作下說明。
WriteServerList:可寫的Redis鏈接地址。
ReadServerList:可讀的Redis鏈接地址。
MaxWritePoolSize:最大寫鏈接數。
MaxReadPoolSize:最大讀鏈接數。
AutoStart:自動重啟。
LocalCacheTime:本地緩存到期時間,單位:秒。
RecordeLog:是否記錄日志,該設置僅用於排查redis運行時出現的問題,如redis工作正常,請關閉該項。
RedisConfigInfo類是記錄redis連接信息,此信息和配置文件中的RedisConfig相呼應。cs代碼如下:(這段代碼不是我自己寫的,但是我覺得應該這樣設計。所以,copy了一下別人的代碼。)

1 using System.Configuration; 2 3 namespace RedisDemo.Common.Redis 4 { 5 public sealed class RedisConfigInfo : ConfigurationSection 6 { 7 public static RedisConfigInfo GetConfig() 8 { 9 var section = (RedisConfigInfo)ConfigurationManager.GetSection("RedisConfig"); 10 return section; 11 } 12 public static RedisConfigInfo GetConfig(string sectionName) 13 { 14 var section = (RedisConfigInfo)ConfigurationManager.GetSection("RedisConfig"); 15 if (section == null) 16 { 17 throw new ConfigurationErrorsException("Section " + sectionName + " is not found."); 18 } 19 return section; 20 } 21 /// <summary> 22 /// 可寫的Redis鏈接地址 23 /// </summary> 24 [ConfigurationProperty("WriteServerList", IsRequired = false)] 25 public string WriteServerList 26 { 27 get 28 { 29 return (string)base["WriteServerList"]; 30 } 31 set 32 { 33 base["WriteServerList"] = value; 34 } 35 } 36 /// <summary> 37 /// 可讀的Redis鏈接地址 38 /// </summary> 39 [ConfigurationProperty("ReadServerList", IsRequired = false)] 40 public string ReadServerList 41 { 42 get 43 { 44 return (string)base["ReadServerList"]; 45 } 46 set 47 { 48 base["ReadServerList"] = value; 49 } 50 } 51 /// <summary> 52 /// 最大寫鏈接數 53 /// </summary> 54 [ConfigurationProperty("MaxWritePoolSize", IsRequired = false, DefaultValue = 5)] 55 public int MaxWritePoolSize 56 { 57 get 58 { 59 var maxWritePoolSize = (int)base["MaxWritePoolSize"]; 60 return maxWritePoolSize > 0 ? maxWritePoolSize : 5; 61 } 62 set 63 { 64 base["MaxWritePoolSize"] = value; 65 } 66 } 67 /// <summary> 68 /// 最大讀鏈接數 69 /// </summary> 70 [ConfigurationProperty("MaxReadPoolSize", IsRequired = false, DefaultValue = 5)] 71 public int MaxReadPoolSize 72 { 73 get 74 { 75 var maxReadPoolSize = (int)base["MaxReadPoolSize"]; 76 return maxReadPoolSize > 0 ? maxReadPoolSize : 5; 77 } 78 set 79 { 80 base["MaxReadPoolSize"] = value; 81 } 82 } 83 /// <summary> 84 /// 自動重啟 85 /// </summary> 86 [ConfigurationProperty("AutoStart", IsRequired = false, DefaultValue = true)] 87 public bool AutoStart 88 { 89 get 90 { 91 return (bool)base["AutoStart"]; 92 } 93 set 94 { 95 base["AutoStart"] = value; 96 } 97 } 98 /// <summary> 99 /// 本地緩存到期時間,單位:秒 100 /// </summary> 101 [ConfigurationProperty("LocalCacheTime", IsRequired = false, DefaultValue = 36000)] 102 public int LocalCacheTime 103 { 104 get 105 { 106 return (int)base["LocalCacheTime"]; 107 } 108 set 109 { 110 base["LocalCacheTime"] = value; 111 } 112 } 113 /// <summary> 114 /// 是否記錄日志,該設置僅用於排查redis運行時出現的問題,如redis工作正常,請關閉該項 115 /// </summary> 116 [ConfigurationProperty("RecordeLog", IsRequired = false, DefaultValue = false)] 117 public bool RecordeLog 118 { 119 get 120 { 121 return (bool)base["RecordeLog"]; 122 } 123 set 124 { 125 base["RecordeLog"] = value; 126 } 127 } 128 } 129 }
RedisManager類主要是創建鏈接池管理對象的。

1 using System.Linq; 2 using ServiceStack.Redis; 3 using System.Collections.Generic; 4 5 namespace RedisDemo.Common.Redis 6 { 7 public class RedisManager 8 { 9 /// <summary> 10 /// redis配置文件信息 11 /// </summary> 12 private static readonly RedisConfigInfo RedisConfigInfo = RedisConfigInfo.GetConfig(); 13 14 private static PooledRedisClientManager _prcm; 15 16 /// <summary> 17 /// 靜態構造方法,初始化鏈接池管理對象 18 /// </summary> 19 static RedisManager() 20 { 21 CreateManager(); 22 } 23 24 /// <summary> 25 /// 創建鏈接池管理對象 26 /// </summary> 27 private static void CreateManager() 28 { 29 var writeServerList = SplitString(RedisConfigInfo.WriteServerList, ","); 30 var readServerList = SplitString(RedisConfigInfo.ReadServerList, ","); 31 32 _prcm = new PooledRedisClientManager(writeServerList,readServerList, 33 new RedisClientManagerConfig 34 { 35 MaxWritePoolSize = RedisConfigInfo.MaxWritePoolSize, 36 MaxReadPoolSize = RedisConfigInfo.MaxReadPoolSize, 37 AutoStart = RedisConfigInfo.AutoStart, 38 }); 39 } 40 41 private static IEnumerable<string> SplitString(string strSource, string split) 42 { 43 return strSource.Split(split.ToArray()); 44 } 45 46 /// <summary> 47 /// 客戶端緩存操作對象 48 /// </summary> 49 public static IRedisClient GetClient() 50 { 51 if (_prcm == null) 52 { 53 CreateManager(); 54 } 55 return _prcm.GetClient(); 56 } 57 58 } 59 }
實戰:封裝redis對哈希表操作的代碼
實戰中,我們操作redis最好還是要封裝提煉一下的。提煉的目的是為了以后代碼的重用。代碼封裝重用的好處我就不多說了,這個不是本次主要討論的。下面是我所用項目中分裝的代碼,其中有的部分我修改過了,貼出來讓大家看下。個人感覺不錯:^_^
RedisOperatorBase類,是redis操作的基類,繼承自IDisposable接口,主要用於釋放內存。

1 using System; 2 using ServiceStack.Redis; 3 4 namespace RedisDemo.Common.Redis.RedisOperator 5 { 6 public abstract class RedisOperatorBase : IDisposable 7 { 8 protected IRedisClient Redis { get; private set; } 9 private bool _disposed = false; 10 protected RedisOperatorBase() 11 { 12 Redis = RedisManager.GetClient(); 13 } 14 protected virtual void Dispose(bool disposing) 15 { 16 if (!this._disposed) 17 { 18 if (disposing) 19 { 20 Redis.Dispose(); 21 Redis = null; 22 } 23 } 24 this._disposed = true; 25 } 26 public void Dispose() 27 { 28 Dispose(true); 29 GC.SuppressFinalize(this); 30 } 31 /// <summary> 32 /// 保存數據DB文件到硬盤 33 /// </summary> 34 public void Save() 35 { 36 Redis.Save(); 37 } 38 /// <summary> 39 /// 異步保存數據DB文件到硬盤 40 /// </summary> 41 public void SaveAsync() 42 { 43 Redis.SaveAsync(); 44 } 45 } 46 }
HashOperator類,是操作哈希表類。繼承自RedisOperatorBase類,代碼中有詳細注釋,理解起來一目了然。

1 using System; 2 using System.Collections.Generic; 3 using ServiceStack.Text; 4 5 namespace RedisDemo.Common.Redis.RedisOperator 6 { 7 public class HashOperator : RedisOperatorBase 8 { 9 public HashOperator() : base() { } 10 /// <summary> 11 /// 判斷某個數據是否已經被緩存 12 /// </summary> 13 public bool Exist<T>(string hashId, string key) 14 { 15 return Redis.HashContainsEntry(hashId, key); 16 } 17 /// <summary> 18 /// 存儲數據到hash表 19 /// </summary> 20 public bool Set<T>(string hashId, string key, T t) 21 { 22 var value = JsonSerializer.SerializeToString<T>(t); 23 return Redis.SetEntryInHash(hashId, key, value); 24 } 25 /// <summary> 26 /// 移除hash中的某值 27 /// </summary> 28 public bool Remove(string hashId, string key) 29 { 30 return Redis.RemoveEntryFromHash(hashId, key); 31 } 32 /// <summary> 33 /// 移除整個hash 34 /// </summary> 35 public bool Remove(string key) 36 { 37 return Redis.Remove(key); 38 } 39 /// <summary> 40 /// 從hash表獲取數據 41 /// </summary> 42 public T Get<T>(string hashId, string key) 43 { 44 string value = Redis.GetValueFromHash(hashId, key); 45 return JsonSerializer.DeserializeFromString<T>(value); 46 } 47 /// <summary> 48 /// 獲取整個hash的數據 49 /// </summary> 50 public List<T> GetAll<T>(string hashId) 51 { 52 var result = new List<T>(); 53 var list = Redis.GetHashValues(hashId); 54 if (list != null && list.Count > 0) 55 { 56 list.ForEach(x => 57 { 58 var value = JsonSerializer.DeserializeFromString<T>(x); 59 result.Add(value); 60 }); 61 } 62 return result; 63 } 64 /// <summary> 65 /// 設置緩存過期 66 /// </summary> 67 public void SetExpire(string key, DateTime datetime) 68 { 69 Redis.ExpireEntryAt(key, datetime); 70 } 71 } 72 }
實戰:redis操作hash哈希表的增刪改查
本來打算這部分把我demo中的操作代碼貼出來的,想想,全是代碼,看着都煩,還不如講解一下這部分的操作對應於redis客戶端操作命令呢。還有一個原因 就是,上面都已經對hash操作進行了分裝,其實如果貼代碼也就是調用封裝的代碼罷了。感覺沒啥意思,相信大家都會調用。沒啥好講的。那么接下來我就說 下,上面封裝的代碼與客戶端操作的對應關系。
Exist<T>方法:對應於redis操作的hexists。返回字段是否是 key 指定的哈希集中存在的字段。true:存在 false:不存在
Set<T>方法:對應於redis操作的hget。設置 key 指定的哈希集中指定字段的值。如果 key 指定的哈希集不存在,會創建一個新的哈希集並與 key 關聯。如果字段在哈希集中存在,它將被重寫。
Remove(string hashId, string key)方法:對應於redis操作的hdel。從 key 指定的哈希集中移除指定的域。在哈希集中不存在的域將被忽略。如果 key 指定的哈希集不存在,它將被認為是一個空的哈希集,該命令將返回false。
Remove(string key)方法:對應於redis操作的del。直接刪除key。
Get<T>方法:對應於redis操作的hget。返回該字段所關聯的值。
GetAll<T>方法:對應於redis操作的hvals。獲取哈希集中的值的列表,當 key 指定的哈希集不存在時返回空列表。
SetExpire方法:對應於redis操作的expire。設置緩存過期。
總結
redis操作很多很多,其實說是封裝,也只是封裝其中的一些常用操作。有興趣的朋友可以用反編譯工具去看下源碼,就可以知道所有操作對應於redis 操作命令了。其實我個人覺得,使用C#操作redis只是語言需要,我們還是要學習它的客戶端操作的。開發中,很多時候調試很慢的,我們可以直接通過 redis客戶端操作去找,這樣效率會更高一點。而且當操作命令熟練的時候,你會發現,客戶端操作比調試操作快很多很多。所以,建議大家多多使用客戶端操 作。剛開始會感覺很多需要記,熟能生巧,多操作,自然會很好。
在redis學習過程中,希望大神們能不吝賜教。同時也希望和我一樣,在研究路上的同仁,有好的見解或是資料能跟小弟分享下,大家一起探討學習下。我的QQ聯系方式:296319075 隨時聯系,備注注明園友就好。大家秉承共同探討、共同進步!如有轉載,請注明出處,謝謝!^_^
還有,如果大家想要源代碼的,可以說下,我會把自己用的放到網盤中或者通過QQ等其他方式發給需要的你。