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.x1.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;
            }
        }
    }
}
posted @ 2026-04-29 11:43  VipSoft  阅读(4)  评论(0)    收藏  举报