FreeRedis Helper
对于 FreeRedis 的版本选择,最直接的答案是:使用最新稳定版 1.5.5。
下面是选择它的理由和详细的版本分析,方便你做出判断。
📊 版本选择建议
| 版本系列 | 推荐程度 | 说明 |
|---|---|---|
| v1.5.x | ⭐⭐⭐⭐⭐ 强烈推荐 | 当前最新主线版本,功能最全、性能最好、Bug修复最及时,适合绝大多数新项目。 |
| v1.4.x | ⭐⭐⭐ 不推荐新项目 | 这是较旧的稳定分支,仅当你的项目有特殊的兼容性要求时(如对AOT编译有特定限制)才考虑。 |
| v1.3.x 及更早 | ⭐ 不推荐 | 版本较老,缺乏很多新特性和性能优化,不建议在新项目中使用。 |
🔍 详细分析
1. 为什么要选最新稳定版?
- 最新版本
1.5.5发布于 2025-12-10,功能最完善,性能最佳。 - 修复所有已知Bug:较新的版本会包含对之前问题的修复。
- 全面支持 Redis 新特性:FreeRedis 从设计之初就紧跟 Redis 的发展,v1.5.x 系列对 RESP3 协议、客户端缓存等特性有非常好的支持。
- 活跃维护:项目更新频繁,选择最新版意味着你的技术债务最少。
2. 有什么潜在风险吗?
使用最新稳定版的风险极低。FreeRedis 的作者是 .NET 开源社区非常有经验的开发者(也是 CSRedisCore 和 FreeSql 的作者),项目质量和代码规范有保障。同时,它有庞大的单元测试(超过268个)作为质量保证,并且从 NuGet 上看,已有超过145万次下载,被大量项目验证过。
📦 如何安装
在你的项目中,通过 NuGet 包管理器控制台执行以下命令即可:
PM> Install-Package FreeRedis -Version 1.5.5
或者,如果你在 .csproj 项目文件中手动管理包引用,可以添加以下条目:
<PackageReference Include="FreeRedis" Version="1.5.5" />
💡 总结
- 对于新项目:直接使用
1.5.5。 - 对于现有项目:如果使用的是
1.4.x或1.3.x,可以放心升级到1.5.5。FreeRedis 版本间的 API 变化非常小,升级几乎是无痛的。
FreeRedis 本身已经内置了连接池,默认配置下:
max poolsize = 100(最大连接数)
min poolsize = 5(最小连接数)
FreeRedisConfig.cs
namespace VipSoft.FreeRedis
{
/// <summary>
/// FreeRedis 配置类
/// </summary>
public class FreeRedisConfig
{
/// <summary>
/// Redis 连接字符串,格式:127.0.0.1:6379,password=123,defaultDatabase=0
/// </summary>
public string ConnectionString { get; set; } = "127.0.0.1:6379";
/// <summary>
/// 连接池最大数量,默认 100
/// </summary>
public int MaxPoolSize { get; set; } = 100;
/// <summary>
/// 连接池最小数量,默认 5
/// </summary>
public int MinPoolSize { get; set; } = 5;
/// <summary>
/// 连接超时时间(毫秒),默认 10000
/// </summary>
public int ConnectTimeout { get; set; } = 10000;
/// <summary>
/// 空闲连接超时时间(毫秒),默认 20000
/// </summary>
public int IdleTimeout { get; set; } = 20000;
/// <summary>
/// Key 前缀(可选),所有操作的 key 会自动添加此前缀
/// </summary>
public string KeyPrefix { get; set; } = string.Empty;
/// <summary>
/// 是否启用 SSL,默认 false
/// </summary>
public bool Ssl { get; set; } = false;
/// <summary>
/// Redis 密码
/// </summary>
public string Password { get; set; } = string.Empty;
/// <summary>
/// 默认数据库索引,默认 0
/// </summary>
public int DefaultDatabase { get; set; } = 0;
}
}
RedisHelper.cs
using System;
using System.Collections.Generic;
using FreeRedis;
using NLog;
using System.Collections.Concurrent;
using System.Configuration;
using System.Linq;
using Newtonsoft.Json;
namespace VuoSift.FreeRedis
{
/// <summary>
/// RedisHelper 单例类(支持多数据库实例)
/// </summary>
public class RedisHelper : IDisposable
{
// 存储不同数据库索引的实例
private static readonly ConcurrentDictionary<int, RedisHelper> _instances = new ConcurrentDictionary<int, RedisHelper>();
private RedisClient _redisClient;
private FreeRedisConfig _config;
private bool _disposed = false;
private readonly object _lockObj = new object();
private readonly int _databaseIndex;
// 日志
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// 私有构造函数,支持指定数据库索引
/// </summary>
private RedisHelper(int databaseIndex = 0)
{
_databaseIndex = databaseIndex;
var config = new FreeRedisConfig
{
ConnectionString = ConfigurationManager.AppSettings["RedisHost"] ?? "127.0.0.1:6379",
Password = ConfigurationManager.AppSettings["RedisPassword"] ?? "",
DefaultDatabase = databaseIndex, // 使用传入的数据库索引作为默认数据库
};
Initialize(config);
}
/// <summary>
/// 获取指定数据库的 RedisHelper 实例
/// </summary>
/// <param name="databaseIndex">数据库索引(0-15)</param>
/// <returns>RedisHelper 实例</returns>
public static RedisHelper GetInstance(int databaseIndex = 0)
{
return _instances.GetOrAdd(databaseIndex, idx => new RedisHelper(idx));
}
/// <summary>
/// 初始化 Redis 连接
/// </summary>
public void Initialize(FreeRedisConfig config = null)
{
if (_redisClient != null)
{
logger.Warn($"RedisHelper (DB:{_databaseIndex}) 已经初始化过,跳过重复初始化");
return;
}
lock (_lockObj)
{
if (_redisClient != null) return;
_config = config ?? new FreeRedisConfig();
// 确保使用实例的数据库索引
if (_config.DefaultDatabase != _databaseIndex)
{
_config.DefaultDatabase = _databaseIndex;
}
try
{
var connectionString = BuildConnectionString();
_redisClient = new RedisClient(connectionString);
// 配置序列化(重要!) -- 为了兼容老数据,这边放方法里去使用
//_redisClient.Serialize = obj =>
//{
// if (obj == null) return null;
// // 使用 System.Text.Json
// return System.Text.Json.JsonSerializer.Serialize(obj);
//};
//_redisClient.Deserialize = (json, type) =>
//{
// if (string.IsNullOrEmpty(json)) return null;
// // 使用 System.Text.Json
// return System.Text.Json.JsonSerializer.Deserialize(json, type);
//};
// 或者使用 Newtonsoft.Json
// _redisClient.Serialize = obj => Newtonsoft.Json.JsonConvert.SerializeObject(obj);
// _redisClient.Deserialize = (json, type) => Newtonsoft.Json.JsonConvert.DeserializeObject(json, type);
// 测试连接
var pingResult = _redisClient.Ping();
if (pingResult == "PONG")
{
logger.Info($"Redis 连接成功,数据库: {_databaseIndex}");
}
else
{
logger.Error($"Redis Ping 失败,数据库: {_databaseIndex}");
}
}
catch (Exception ex)
{
logger.Error(ex, $"Redis 初始化失败,数据库: {_databaseIndex}");
throw;
}
}
}
private string BuildConnectionString()
{
var parts = new List<string>();
// 基础地址
parts.Add(_config.ConnectionString.Split(',')[0]);
// 密码
if (!string.IsNullOrEmpty(_config.Password))
parts.Add($"password={_config.Password}");
// 默认数据库
if (_config.DefaultDatabase != 0)
parts.Add($"defaultDatabase={_config.DefaultDatabase}");
// 连接池配置
if (_config.MaxPoolSize != 100)
parts.Add($"max poolsize={_config.MaxPoolSize}");
if (_config.MinPoolSize != 5)
parts.Add($"min poolsize={_config.MinPoolSize}");
// 超时配置
if (_config.ConnectTimeout != 10000)
parts.Add($"connectTimeout={_config.ConnectTimeout}");
if (_config.IdleTimeout != 20000)
parts.Add($"idleTimeout={_config.IdleTimeout}");
// SSL
if (_config.Ssl)
parts.Add($"ssl=true");
// Key 前缀
if (!string.IsNullOrEmpty(_config.KeyPrefix))
parts.Add($"prefix={_config.KeyPrefix}");
return string.Join(",", parts);
}
/// <summary>
/// 获取当前实例使用的数据库索引
/// </summary>
public int DatabaseIndex => _databaseIndex;
/// <summary>
/// 获取原始 RedisClient(推荐直接使用)
/// </summary>
public RedisClient GetClient()
{
if (_redisClient == null)
throw new InvalidOperationException($"RedisHelper (DB:{_databaseIndex}) 未初始化,请先调用 Initialize 方法");
return _redisClient;
}
/// <summary>
/// 获取指定数据库的客户端(临时切换数据库)
/// </summary>
public RedisClient GetDatabase(int dbIndex)
{
return GetClient().GetDatabase(dbIndex);
}
/// <summary>
/// 执行操作(自动管理数据库切换)
/// </summary>
public T Execute<T>(int dbIndex, Func<RedisClient, T> action)
{
using (var db = GetDatabase(dbIndex))
{
return action(db);
}
}
/// <summary>
/// 执行操作(无返回值)
/// </summary>
public void Execute(int dbIndex, Action<RedisClient> action)
{
using (var db = GetDatabase(dbIndex))
{
action(db);
}
}
/// <summary>
/// 序列化对象
/// </summary>
private string SerializeValue<T>(T value)
{
if (value == null)
return null;
// 如果是字符串或基础类型,直接转换为字符串
if (typeof(T) == typeof(string))
return value as string;
if (typeof(T).IsPrimitive || typeof(T) == typeof(decimal))
return Convert.ToString(value);
// 复杂类型使用 JSON 序列化
return JsonConvert.SerializeObject(value);
}
/// <summary>
/// 反序列化对象
/// </summary>
private T DeserializeValue<T>(string value)
{
if (string.IsNullOrEmpty(value))
return default(T);
// 如果是字符串类型,直接返回
if (typeof(T) == typeof(string))
return (T)(object)value;
// 如果是基础类型,尝试转换
if (typeof(T).IsPrimitive || typeof(T) == typeof(decimal))
{
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
return default(T);
}
}
// 复杂类型使用 JSON 反序列化
return JsonConvert.DeserializeObject<T>(value);
}
#region 基础操作(默认使用实例的数据库)
/// <summary>
/// 设置键值对
/// </summary>
public void Set<T>(string key, T value, int? expireSeconds = null)
{
var serializedFields = SerializeValue(value);
if (expireSeconds.HasValue)
GetClient().Set(key, serializedFields, expireSeconds.Value);
else
GetClient().Set(key, serializedFields);
}
/// <summary>
/// 设置键值对 -- 兼容老代码
/// </summary>
public void SetBySeconds<T>(string key, T value, int? expireSeconds = null)
{
var serializedFields = SerializeValue(value);
if (expireSeconds.HasValue)
GetClient().Set(key, serializedFields, expireSeconds.Value);
else
GetClient().Set(key, serializedFields);
}
/// <summary>
/// 设置键值对(带过期时间)
/// </summary>
public bool SetNx<T>(string key, T value, int expireSeconds)
{
return GetClient().SetNx(key, value, expireSeconds);
}
/// <summary>
/// 获取键对应的值
/// </summary>
public T Get<T>(string key)
{
var value = GetClient().Get<string>(key);
if (value == null)
{
return default(T);
}
return DeserializeValue<T>(value);
}
/// <summary>
/// 获取键对应的值,如果不存在返回默认值
/// </summary>
public T Get<T>(string key, T defaultValue)
{
var value = GetClient().Get<T>(key);
if (value == null)
return defaultValue;
return value;
}
/// <summary>
/// 删除指定键
/// </summary>
public long Del(params string[] keys)
{
return GetClient().Del(keys);
}
/// <summary>
/// 设置过期时间(秒)
/// </summary>
public bool Expire(string key, int seconds)
{
return GetClient().Expire(key, seconds);
}
/// <summary>
/// 获取键的剩余过期时间(秒)
/// </summary>
public long Ttl(string key)
{
return GetClient().Ttl(key);
}
/// <summary>
/// 递增
/// </summary>
public long Incr(string key)
{
return GetClient().Incr(key);
}
/// <summary>
/// 递增指定步长
/// </summary>
public long IncrBy(string key, long increment)
{
return GetClient().IncrBy(key, increment);
}
/// <summary>
/// 递减
/// </summary>
public long Decr(string key)
{
return GetClient().Decr(key);
}
/// <summary>
/// 递减指定步长
/// </summary>
public long DecrBy(string key, long decrement)
{
return GetClient().DecrBy(key, decrement);
}
#endregion
#region 哈希表操作(支持对象序列化)
/// <summary>
/// 哈希表:看 hash key 存不存在
/// </summary>
public long HDel(string key, string field)
{
var result = 0L;
try
{
result = GetClient().HDel(key, field);
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
return result;
}
/// <summary>
/// 哈希表:看 hash key 存不存在
/// </summary>
public bool HExists(string key, string field)
{
var result = false;
try
{
result = GetClient().HExists(key, field);
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
return result;
}
/// <summary>
/// 哈希表:设置字段值(自动处理对象序列化)
/// </summary>
public bool HSet<T>(string key, string field, T value)
{
var result = false;
try
{
var serializedValue = SerializeValue(value);
result = GetClient().HSet(key, field, serializedValue) > 0;
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
return result;
}
/// <summary>
/// 哈希表:获取字段值(自动处理对象反序列化)
/// </summary>
public T HGet<T>(string key, string field)
{
var value = GetClient().HGet<string>(key, field);
if (value == null)
return default(T);
return DeserializeValue<T>(value);
}
public long HMSet<T>(string key, Dictionary<string, T> fields)
{
var result = 0L;
try
{
var serializedFields = new Dictionary<string, string>();
foreach (var kvp in fields)
{
serializedFields[kvp.Key] = SerializeValue(kvp.Value);
}
// 尝试使用 HMSet
GetClient().HMSet(key, serializedFields);
result = serializedFields.Count;
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
return result;
}
/// <summary>
/// 哈希表:获取所有字段和值(自动处理对象反序列化)
/// </summary>
public Dictionary<string, T> HGetAll<T>(string key)
{
try
{
var dict = GetClient().HGetAll<string>(key);
if (dict == null || dict.Count == 0)
return new Dictionary<string, T>();
var result = new Dictionary<string, T>();
foreach (var kvp in dict)
{
result[kvp.Key] = DeserializeValue<T>(kvp.Value);
}
return result;
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
return null;
}
public List<T> HGetValues<T>(string key)
{
try
{
var dictionary = HGetAll<T>(key);
if (dictionary == null || dictionary.Count == 0)
{
return new List<T>();
}
return dictionary.Values.ToList();
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
return null;
}
#endregion
#region 列表操作
/// <summary>
/// 列表:左侧推入
/// </summary>
public long LPush<T>(string key, params T[] values)
{
return GetClient().LPush(key, values);
}
/// <summary>
/// 列表:右侧推入
/// </summary>
public long RPush<T>(string key, params T[] values)
{
return GetClient().RPush(key, values);
}
public List<T> LPop<T>(string key)
{
try
{
var value = GetClient().LPop<string>(key);
if (value == null)
return default(List<T>);
return DeserializeValue<List<T>>(value);
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
return null;
}
/// <summary>
/// 列表:右侧弹出
/// </summary>
public List<T> RPop<T>(string key)
{
try
{
var value = GetClient().RPop<string>(key);
if (value == null)
return default(List<T>);
return DeserializeValue<List<T>>(value);
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
return null;
}
/// <summary>
/// 列表:获取指定范围的元素
/// </summary>
public T[] LRange<T>(string key, long start, long stop)
{
return GetClient().LRange<T>(key, start, stop);
}
/// <summary>
/// 列表:获取列表长度
/// </summary>
public long LLen(string key)
{
return GetClient().LLen(key);
}
#endregion
#region 集合操作
/// <summary>
/// 集合:添加成员
/// </summary>
public long SAdd<T>(string key, params T[] members)
{
return GetClient().SAdd(key, members);
}
/// <summary>
/// 集合:获取所有成员
/// </summary>
public T[] SMembers<T>(string key)
{
return GetClient().SMembers<T>(key);
}
/// <summary>
/// 集合:判断是否包含成员
/// </summary>
public bool SIsMember<T>(string key, T member)
{
return GetClient().SIsMember(key, member);
}
/// <summary>
/// 集合:删除成员
/// </summary>
public long SRem<T>(string key, params T[] members)
{
return GetClient().SRem(key, members);
}
/// <summary>
/// 集合:获取集合大小
/// </summary>
public long SCard(string key)
{
return GetClient().SCard(key);
}
#endregion
#region 有序集合操作
/// <summary>
/// 有序集合:添加带分数的成员
/// </summary>
public bool ZAdd(string key, decimal score, string member)
{
return GetClient().ZAdd(key, score, member) > 0;
}
/// <summary>
/// 有序集合:添加带分数的成员(double 重载)
/// </summary>
public bool ZAdd(string key, double score, string member)
{
return GetClient().ZAdd(key, (decimal)score, member) > 0;
}
/// <summary>
/// 有序集合:按分数范围获取成员
/// </summary>
public string[] ZRangeByScore(string key, decimal min, decimal max, int? limit = null, int offset = 0)
{
if (limit.HasValue)
return GetClient().ZRangeByScore(key, min, max, limit.Value, offset);
return GetClient().ZRangeByScore(key, min, max);
}
/// <summary>
/// 有序集合:按分数范围获取成员(double 重载)
/// </summary>
public string[] ZRangeByScore(string key, double min, double max, int? limit = null, int offset = 0)
{
return ZRangeByScore(key, (decimal)min, (decimal)max, limit, offset);
}
/// <summary>
/// 有序集合:获取成员分数
/// </summary>
public decimal? ZScore(string key, string member)
{
return GetClient().ZScore(key, member);
}
/// <summary>
/// 有序集合:删除成员
/// </summary>
public long ZRem(string key, params string[] members)
{
return GetClient().ZRem(key, members);
}
/// <summary>
/// 有序集合:获取有序集合的大小
/// </summary>
public long ZCard(string key)
{
return GetClient().ZCard(key);
}
/// <summary>
/// 有序集合:增加成员的分数
/// </summary>
public decimal ZIncrBy(string key, decimal increment, string member)
{
return GetClient().ZIncrBy(key, increment, member);
}
/// <summary>
/// 有序集合:按排名范围获取成员
/// </summary>
public string[] ZRange(string key, long start, long stop)
{
return GetClient().ZRange(key, start, stop);
}
/// <summary>
/// 有序集合:获取成员的排名(从0开始)
/// </summary>
public long? ZRank(string key, string member)
{
return GetClient().ZRank(key, member);
}
/// <summary>
/// 有序集合:获取成员的倒序排名(从0开始)
/// </summary>
public long ZRevRank(string key, string member)
{
return GetClient().ZRevRank(key, member);
}
#endregion
#region Lua 脚本
/// <summary>
/// 执行 Lua 脚本
/// </summary>
public object Eval(string script, params string[] keys)
{
return GetClient().Eval(script, keys);
}
/// <summary>
/// 执行 Lua 脚本(带 KEYS 参数)
/// </summary>
public object Eval(string script, string[] keys, params object[] args)
{
return GetClient().Eval(script, keys, args);
}
#endregion
public void Dispose()
{
if (_disposed) return;
lock (_lockObj)
{
if (_disposed) return;
_redisClient?.Dispose();
_redisClient = null;
_disposed = true;
}
}
}
}
本文来自博客园,作者:VipSoft 转载请注明原文链接:https://www.cnblogs.com/vipsoft/p/19950714
浙公网安备 33010602011771号