一、安装第三方库
二、官网
StackExchange.Redis |通用型 redis 客户端
三、连接示例
private static string redisConnectionString = "localhost:6379,defaultDatabase=0,allowAdmin=true,asyncTimeout=10000,password=123456789";
private static string redisConnectionString = "localhost:6379,defaultDatabase=0,allowAdmin=true,asyncTimeout=10000";
四、封装:增删改查
public class RedisData
{
/// <summary>
/// 键(包含冒号前的内容)
/// </summary>
public string Key { get; set; }
/// <summary>
/// 键尾
/// </summary>
public string KeyTail { get; set; }
/// <summary>
/// 值(str)
/// </summary>
public string Value { get; set; }
/// <summary>
/// 值(List Set)
/// </summary>
public List<string> ListValue { get; set; } = new List<string>();
/// <summary>
/// 值(Hash,即Dictionary)
/// </summary>
public Dictionary<string, string> HashDictValue { get; set; } = new Dictionary<string, string>();
/// <summary>
/// 值(SortedSet)
/// </summary>
public Dictionary<string, double> SortedSetValue { get; set; } = new Dictionary<string, double>();
/// <summary>
/// 值类型[包含:字符串,列表,无序集合(Set),有序集合(SortedSet),哈希(字典),流]
/// </summary>
public RedisType ValueType { get; set; }
/// <summary>
/// 剩余过期时间(天)
/// </summary>
public int? TTLDays { get; set; }
/// <summary>
/// 剩余过期时间(分钟)
/// </summary>
public int? TTLMins { get; set; }
/// <summary>
/// 剩余过期时间(秒)
/// </summary>
public int? TTLSecs { get; set; }
/// <summary>
/// 到期时间
/// </summary>
public DateTime? ExpirationTime { get; set; }
/// <summary>
/// 查询时间
/// </summary>
public DateTime QueryTime { get; set; }
}
public class RedisConn
{
public string address { get; set; } = "localhost";
public int port { get; set; } = 6379;
public int database { get; set; } = 0;
public string pwd { get; set; }
}
public static class RedisExtension
{
private static RedisConn Conn = new RedisConn();
// 默认的 Redis 连接字符串
private static string redisConnectionString
{
get => $"{Conn.address}:{Conn.port},defaultDatabase={Conn.database},allowAdmin=true,asyncTimeout=10000,password={Conn.pwd}";
set
{
// 当连接字符串被修改时,我们需要重新初始化 Lazy<ConnectionMultiplexer> 以确保新的连接字符串生效
_redis = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(value));
}
}
// 使用 Lazy 实例化连接,连接字符串可以被动态修改
private static Lazy<ConnectionMultiplexer> _redis = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(redisConnectionString));
// 通过连接获取 Redis 数据库
private static IDatabase db => _redis.Value.GetDatabase();
// 获取 Redis 服务器实例
private static IServer server => _redis.Value.GetServer(Conn.address, Conn.port);
// 更新 Redis 连接字符串的方法
public static (RedisConn Conn, string redisConnectionString) UpdateRedisConnectionString(string address, int port, int database,string pwd)
{
Conn.address = address;
Conn.port = port;
Conn.database = database;
Conn.pwd = pwd;
// 更新连接字符串
redisConnectionString = $"{Conn.address}:{Conn.port},defaultDatabase={Conn.database},allowAdmin=true,asyncTimeout=10000,password={Conn.pwd}";
return (Conn:Conn,redisConnectionString:redisConnectionString);
}
/// <summary>
/// 【配置】尝试连接
/// </summary>
/// <param name="conn"></param>
/// <returns></returns>
public static async Task<bool> TryConnectAsync(RedisConn conn = null)
{
// 如果传入了新的连接字符串,更新 Redis 连接
if (conn != null)
{
Conn = conn;
}
try
{
// 通过插入 测试 Redis 连接
string testKey = Guid.NewGuid().ToString();
await SaveAsync(testKey, testKey);
await DeleteAsync(testKey);
return true; // 连接成功
}
catch (Exception ex)
{
return false; // 连接失败
}
}
/// <summary>
/// 【配置】获取redis数据库
/// </summary>
/// <returns></returns>
public static async Task<IDatabase> GetDB()
{
return db;
}
/// <summary>
/// 【配置】获取所有有数据的数据库名称
/// </summary>
/// <returns></returns>
public static async Task<List<string>> GetUsedDB()
{
// 获取数据库的键空间信息 本质是执行INFO keyspace
var info = server.Info("keyspace");
if (!info.Any())
{
return new List<string>();
}
var list = info[0].Select(x => x.Key.ToString()).ToList();
return list;
}
/// <summary>
/// 【配置】获取redis服务
/// </summary>
/// <returns></returns>
public static async Task<IServer> GetServer()
{
return server;
}
/// <summary>
/// 【查询】符合条件的键(默认不传查所有)
/// </summary>
/// <param name="pattern">示例查找分类可以传入:"Transport:*"</param>
/// <returns></returns>
public static List<string> GetKeys(string pattern = "")
{
if (string.IsNullOrEmpty(pattern))
{
return server.Keys().Select(x => x.ToString()).ToList();
}
return server.Keys(pattern: pattern).Select(x => x.ToString()).ToList();
}
/// <summary>
/// 【查询】指定键的键值对信息
/// </summary>
/// <param name="key"></param>
public static async Task<RedisData> GetByKeyAsync(string key)
{
if (await db.KeyExistsAsync(key)) // 键存在
{
var valueType = await db.KeyTypeAsync(key);
var listValue = new List<string>();
if (valueType == RedisType.List)
{
listValue = (await db.ListRangeAsync(key)).Select(x => x.ToString()).ToList();
}
else if (valueType == RedisType.Set)
{
listValue = (await db.SetMembersAsync(key)).Select(x => x.ToString()).ToList();
}
RedisData data = new RedisData()
{
Key = key,
Value = valueType == RedisType.String ? (await db.StringGetAsync(key)).ToString() : "",
ValueType = valueType,
ListValue = listValue,
SortedSetValue = valueType == RedisType.SortedSet ? (await db.SortedSetRangeByRankWithScoresAsync(key)).ToDictionary(x => x.Element.ToString(), x => x.Score) : new Dictionary<string, double>(),
HashDictValue = valueType == RedisType.Hash ? (await db.HashGetAllAsync(key)).ToDictionary(x => x.Name.ToString(), x => x.Value.ToString()) : new Dictionary<string, string>(),
KeyTail = key.Split(':').LastOrDefault(),
QueryTime = DateTime.Now
};
var ttl = await db.KeyTimeToLiveAsync(key);
if (ttl != null)
{
data.ExpirationTime = DateTime.Now.Add(ttl.Value);
data.TTLDays = Convert.ToInt32(ttl.Value.TotalDays);
data.TTLMins = Convert.ToInt32(ttl.Value.TotalMinutes);
data.TTLSecs = Convert.ToInt32(ttl.Value.TotalSeconds);
}
return data;
}
return null;
}
/// <summary>
/// 【查询】所有键的键值对信息
/// </summary>
/// <param name="key"></param>
public static async Task<List<RedisData>> GetAllAsync()
{
var allKeys = GetKeys();
var list = new List<RedisData>();
foreach (var key in allKeys)
{
var data = await GetByKeyAsync(key);
if (data != null)
{
list.Add(data);
}
}
return list;
}
/// <summary>
/// 【删除】指定键,只有在键存在时才删除
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static async Task<bool> DeleteAsync(string key)
{
if (await db.KeyExistsAsync(key)) // 判断键是否存在
{
return await db.KeyDeleteAsync(key); // 如果存在,删除键
}
return false; // 如果键不存在,返回 false
}
/// <summary>
/// 【自定义指令(仅支持简单的,没有空格的命令)】
/// </summary>
/// 示例输入:PING INFO(查看配置信息) FLUSHDB(删除当前数据库所有键值对) FLUSHALL(删除所有数据库键值对)
/// <returns></returns>
public static async Task<string> SendCommandAsync(string command)
{
string result = "";
try
{
result = (await db.ExecuteAsync(command)).ToString();
return result; // 返回响应
}
catch (Exception ex)
{
return ex.Message.ToString();
}
}
/// <summary>
/// 【获取INFO配置信息】
/// </summary>
/// <returns></returns>
public static async Task<Dictionary<string, string>> GetInfo()
{
var splitlabel = ":";
var infos = await SendCommandAsync("INFO");
var infosList = infos.Split().Where(x => x.Contains(splitlabel));
var showdict = new Dictionary<string, string>();
foreach (var item in infosList)
{
var key = item.Split(splitlabel)[0].Trim();
var value = item.Split(splitlabel)[1].Trim();
switch (key)
{
case "redis_version":
showdict["版本号"] = value;
break;
case "redis_mode":
showdict["模式"] = value;
break;
case "role":
showdict["角色"] = value;
break;
case "process_id":
showdict["进程PID"] = value;
break;
case "tcp_port":
showdict["TCP端口"] = value;
break;
case "uptime_in_days":
showdict["服务已运行天数"] = value;
break;
case "hz":
showdict["每秒执行操作数"] = value;
break;
case "executable":
showdict["执行文件路径"] = value;
break;
case "config_file":
showdict["配置文件路径"] = value;//executable
break;
case "connected_clients":
showdict["当前连接数"] = value;
break;
case "blocked_clients":
showdict["阻塞数量"] = value;
break;
case "used_memory_human":
showdict["总使用内存"] = value;
break;
case "used_memory_rss_human":
showdict["物理内存"] = value;
break;
case "used_memory_peak_human":
showdict["峰值使用内存"] = value;
break;
case "maxmemory_policy":
showdict["内存回收策略"] = value;
break;
case "mem_fragmentation_ratio":
showdict["碎片比例"] = value;
break;
case "mem_allocator":
showdict["内存分配器"] = value;
break;
case "rdb_last_save_time":
showdict["RDB上次成功保存的时间"] = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt32(value)).LocalDateTime.ToString("yyyy年MM月dd日 HH时mm分ss秒");
break;
case "rdb_last_bgsave_status":
showdict["RDB上次保存的状态"] = value;
break;
case "aof_enabled":
showdict["是否启用 AOF 持久化"] = value;
break;
case "expired_keys":
showdict["过期的键数量"] = value;
break;
case "pubsub_channels":
showdict["当前的订阅频道数"] = value;
break;
case "pubsub_patterns":
showdict["当前的订阅模式数"] = value;
break;
case "latest_fork_usec":
showdict["最近一次 fork 操作的耗时(秒)"] = Math.Round(Convert.ToDouble(value) / 1000, 2).ToString();
break;
case "cluster_enabled":
showdict["是否启用集群模式"] = value;
break;
default:
break;
}
}
return showdict;
}
/// <summary>
/// 【保存】无论键是否存在,覆盖一个键值对(值:str)
/// </summary>
/// <param name="key">第一个入参示例“NewYork:School:John”(文件夹形式);第三个入参可以传递形如TimeSpan.FromSeconds(30),传null则表明永不过期</param>
/// <param name="value"></param>
/// <param name="expiration"></param>
public static async Task<bool> SaveAsync(string key, string value, TimeSpan? expiration = null)
{
if (expiration == null) // 不设置超时时间
{
await db.StringSetAsync(key, value);
}
else // 设置超时时间
{
await db.StringSetAsync(key, value, expiration);
}
return true;
}
/// <summary>
/// 【保存】无论键是否存在,覆盖一个键值对(值:List)
/// </summary>
/// <param name="key"></param>
/// <param name="values"></param>
/// <returns></returns>
public static async Task<bool> ListRPUSH(string key, List<string> values)
{
long affectCounts = 0;
foreach (var value in values)
{
affectCounts = await db.ListRightPushAsync(key, value); // 使用 ListRightPushAsync 向列表右端插入
}
return Convert.ToInt32(affectCounts) == values.Count;
}
/// <summary>
/// 【保存】无论键是否存在,覆盖一个键值对(值:无序集合Set)
/// </summary>
/// <param name="key"></param>
/// <param name="values"></param>
/// <returns></returns>
public static async Task<bool> SetSADD(string key, HashSet<string> values)
{
int addedCount = 0;
foreach (var value in values)
{
bool wasAdded = await db.SetAddAsync(key, value);
if (wasAdded || await GetByKeyAsync(value) != null)//如果被添加了或者存在,则都计入有效
{
addedCount++;
}
}
// 添加数量=传入数量,则说明添加成功
return addedCount == values.Count;
}
/// <summary>
/// 【保存】无论键是否存在,覆盖一个键值对(值:有序集合SortedSet,其中值又分为键值对,值的键值对有两种模式:①全覆盖-默认 ②有该键不更新)
/// </summary>
/// <param name="key"></param>
/// <param name="values"></param>
/// <returns></returns>
public static async Task<(int AddCount, int UnAddCount, int UnCoverCount)> SortedSetZADD(string key, Dictionary<string, double> dict, bool isCover = true)
{
int AddCount = 0;
int UpdateCount = 0;
int UnCoverCount = 0;
var keys = (await GetByKeyAsync(key))?.SortedSetValue?.Select(x => x.Key).ToList();
foreach (var kvp in dict)
{
if (!isCover && keys.Contains(kvp.Key))//选择模式2且有序集合的key是重复的
{
UnCoverCount++;
continue;//不覆盖更新
}
bool wasAdded = await db.SortedSetAddAsync(key, kvp.Key, kvp.Value);
if (wasAdded)
{
AddCount++;
}
else
{
UpdateCount++;
}
}
return (AddCount: AddCount, UpdateCount: UpdateCount, UnCoverCount: UnCoverCount);//返回:插入数 更新数 未覆盖数
}
/// <summary>
/// 【保存】无论键是否存在,覆盖一个键值对(值:哈希字典Hast-Dict)
/// </summary>
/// <param name="key"></param>
/// <param name="values"></param>
/// <returns></returns>
public static async Task<bool> HashHSet(string key, Dictionary<string, string> dict, bool isCover = true)
{
try
{
HashEntry[] hashEntries = new HashEntry[dict.Count];
int index = 0;
foreach (var kvp in dict)
{
hashEntries[index++] = new HashEntry(kvp.Key, kvp.Value);
}
await db.HashSetAsync("myHash", hashEntries);
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 【更新】设置指定键的过期时间[第二个入参可以传递形如TimeSpan.FromSeconds(30),传null则表明永不过期]
/// </summary>
public static async Task<bool> SetTTLAsync(string key, TimeSpan? expiration = null)
{
if (expiration == null)
{
return await db.KeyExpireAsync(key, (TimeSpan?)null);
}
return await db.KeyExpireAsync(key, expiration);
}
/// <summary>
/// 初始化测试数据
/// </summary>
/// <returns></returns>
public static async Task<string> InitailTestData()
{
try
{
//初始化前先清空
await RedisExtension.DeleteAsync("myList");
await RedisExtension.DeleteAsync("myString");
await RedisExtension.DeleteAsync("mySet");
await RedisExtension.DeleteAsync("mySortedSet");
await RedisExtension.DeleteAsync("myHash");
await RedisExtension.DeleteAsync("Transport:bike");
await RedisExtension.DeleteAsync("Transport:bus");
await RedisExtension.DeleteAsync("Transport:metro:Line1");
await RedisExtension.DeleteAsync("Transport:metro:Line2");
await RedisExtension.DeleteAsync("Transport:high-speed-rail");
//初始化一个字符串
await RedisExtension.SaveAsync("myString", "2024888");
//初始化字符串(带文件夹)
await RedisExtension.SaveAsync("Transport:bike", "i am a bike");
await RedisExtension.SaveAsync("Transport:bus", "i am a bus");
await RedisExtension.SaveAsync("Transport:metro:Line1", "i am a metro Line1");
await RedisExtension.SaveAsync("Transport:metro:Line2", "i am a metro Line2");
await RedisExtension.SaveAsync("Transport:high-speed-rail", "i am a high-speed rail");
//初始化一个列表List
List<string> values = new List<string> { "item1", "item2", "item3" };
await RedisExtension.ListRPUSH("myList", values);
//初始化一个无序集合Set
HashSet<string> values2 = new HashSet<string> { "apple", "banana", "cherry" };
await RedisExtension.SetSADD("mySet", values2);
//初始化一个有序集合SortedSet
var players = new Dictionary<string, double>
{
{ "player1", 120 },
{ "player2", 100 },
{ "player3", 15 },
{ "player4", 150 }
};
await RedisExtension.SortedSetZADD("mySortedSet", players);
// 初始化一个哈希字典
var myDict = new Dictionary<string, string>
{
{ "name", "John" },
{ "age", "12" },
{ "city", "New York" }
};
await RedisExtension.HashHSet("myHash", myDict);
//查询上面的所有数据
var t1 = await RedisExtension.GetByKeyAsync("myList");
var t2 = await RedisExtension.GetByKeyAsync("myString");
var t3 = await RedisExtension.GetByKeyAsync("mySet");
var t4 = await RedisExtension.GetByKeyAsync("mySortedSet");
var t5 = await RedisExtension.GetByKeyAsync("myHash");
return "初始化成功!";
}
catch (Exception ex)
{
return ex.ToString();
}
}
}
五、测试和运行demo
using ConsoleApp2;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
class Program
{
public static async Task<object> RunMain()
{
await RedisExtension.TryConnectAsync(new RedisConn
{
database = 1 //测试数据库
});
var res= await RedisExtension.InitailTestData();
Console.WriteLine(res);
return null;
}
static void Main(string[] args)
{
var t = Task.Run(async () =>await RunMain()).Result;
}
}