Loading

C#中使用Redis学习二 在.NET4.5中使用redis hash操作

摘要


   上一篇讲述了安装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等其他方式发给需要的你。  

posted @ 2023-03-08 13:13  RuoVea  阅读(24)  评论(0编辑  收藏  举报