StackExchange.Redis 二次封裝

在NuGet直接搜索StackExchange.Redis,下載引用包;git

幫助類github

/***********************************
 * Developer: Lio.Huang
 * Date:2018-11-21
 **********************************/
using System;
using System.Collections.Generic;
using System.Linq;
using StackExchange.Redis;
using Newtonsoft.Json;
using System.Threading.Tasks;

namespace Banana.Utility.Redis
{
    /// <summary>
    /// Redis Base Utils
    /// </summary>
    public class RedisUtils
    {
        /// <summary>  
        /// redis配置文件信息  
        /// </summary>  
        public static string RedisPath = "172.16.3.82:6379";

        /// <summary>
        /// 註冊地址
        /// </summary>
        /// <param name="redisPath">redis connection string</param>
        public static void Register(string redisPath)
        {
            RedisPath = redisPath;
        }

        private static ConnectionMultiplexer _instance = null;

        /// <summary>
        /// 使用一個靜態屬性來返回已鏈接的實例,以下列中所示。這樣,一旦 ConnectionMultiplexer 斷開鏈接,即可以初始化新的鏈接實例。
        /// </summary>
        public static ConnectionMultiplexer Instance
        {
            get
            {
                return conn.Value;
            }
        }

        //public static ConnectionMultiplexer Instance
        //{
        //    get
        //    {
        //        //if (_instance == null)
        //        //{
        //        //    lock (_locker)
        //        //    {
        //        if (_instance == null || !_instance.IsConnected)
        //        {
        //            _instance = ConnectionMultiplexer.Connect(RedisPath);
        //            //註冊以下事件
        //            _instance.ConnectionFailed += MuxerConnectionFailed;
        //            _instance.ConnectionRestored += MuxerConnectionRestored;
        //            _instance.ErrorMessage += MuxerErrorMessage;
        //            _instance.HashSlotMoved += MuxerHashSlotMoved;
        //            _instance.InternalError += MuxerInternalError;
        //        }
        //        //    }
        //        //}
        //        return _instance;
        //    }
        //}

        private static Lazy<ConnectionMultiplexer> conn = new Lazy<ConnectionMultiplexer>(
        () =>
        {
            _instance = ConnectionMultiplexer.Connect(RedisPath);
            //註冊以下事件
            _instance.ConnectionFailed += MuxerConnectionFailed;
            _instance.ConnectionRestored += MuxerConnectionRestored;
            _instance.ErrorMessage += MuxerErrorMessage;
            _instance.HashSlotMoved += MuxerHashSlotMoved;
            _instance.InternalError += MuxerInternalError;
            return _instance;
        }
        );

        #region Keys
        /// <summary>
        /// 判斷鍵是否存在
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key">鍵值</param>
        /// <returns></returns>
        public static bool KeyExists(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.KeyExists(key);
        }

        /// <summary>
        /// 爲指定的鍵設置失效時間
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="expiry">時間間隔</param>
        /// <returns></returns>
        public static bool SetExpire(int dbIndex, string key, TimeSpan? expiry)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.KeyExpire(key, expiry);
        }

        /// <summary>
        ///  爲指定的鍵設置失效時間
        /// </summary> 
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="timeout">時間間隔(秒)</param>
        /// <returns></returns>
        public static bool SetExpire(int dbIndex, string key, int timeout = 0)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.KeyExpire(key, DateTime.Now.AddSeconds(timeout));
        }

        /// <summary>
        ///  刪除鍵
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool KeyDelete(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.KeyDelete(key);
        }

        /// <summary>
        ///  鍵重命名
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="oldKey">舊值</param>
        /// <param name="newKey">新值</param>
        /// <returns></returns>
        public static bool KeyRename(int dbIndex, string oldKey, string newKey)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.KeyRename(oldKey, newKey);
        }
        #endregion

        #region Strings
        /// <summary>
        /// 獲取字符串數據
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key">Redis鍵</param>
        /// <returns></returns>
        public static string StringGet(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            if (db != null)
            {
                return db.StringGet(key);
            }
            return string.Empty;
        }

        /// <summary>
        /// 獲取對象類型數據
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key">Redis鍵</param>
        /// <returns></returns>
        public static T StringGet<T>(int dbIndex, string key) where T : class
        {
            T data = default(T);
            var db = Instance.GetDatabase(dbIndex);
            if (db != null)
            {
                var value = db.StringGet(key);
                return ConvertObj<T>(value);
            }
            return data;
        }

        /// <summary>
        /// 設置值類型的值
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static bool StringSet(int dbIndex, string key, RedisValue value, TimeSpan? expiry)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.StringSet(key, value, expiry);
        }

        /// <summary>
        /// 設置對象類型的值
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static bool StringSet<T>(int dbIndex, string key, T value, TimeSpan? expiry) where T : class
        {
            if (value == default(T))
            {
                return false;
            }
            var db = Instance.GetDatabase(dbIndex);
            return db.StringSet(key, ConvertJson(value), expiry);
        }
        #endregion

        #region Hashes
        /// <summary>
        /// Hash是否存在
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="hashId">HashId</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool HashExists(int dbIndex, string hashId, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.HashExists(key, hashId);
        }

        /// <summary>
        /// Hash長度
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="hashId">HashId</param>
        /// <returns></returns>
        public static long HashLength(int dbIndex, string hashId)
        {
            var db = Instance.GetDatabase(dbIndex);
            var length = db.HashLength(hashId);
            return length;
        }

        /// <summary>
        /// 設置哈希值
        /// </summary>
        /// <typeparam name="T">哈希值類型</typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="hashId">哈希ID</param>
        /// <param name="key"></param>
        /// <param name="t">哈希值</param>
        /// <returns></returns>
        public static bool HashSet<T>(int dbIndex, string hashId, string key, T t)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.HashSet(hashId, key, ConvertJson(t));
        }

        /// <summary>
        ///   獲取哈希值
        /// </summary>
        /// <typeparam name="T">哈希值類型</typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="hashId">哈希ID</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T HashGet<T>(int dbIndex, string hashId, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            string value = db.HashGet(hashId, key);
            if (string.IsNullOrWhiteSpace(value))
                return default(T);
            return ConvertObj<T>(value);
        }

        /// <summary>
        ///   獲取哈希值
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="hashId">哈希ID</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string HashGet(int dbIndex, string hashId, string key)
        {
            var db = Instance.GetDatabase(dbIndex); 
            return db.HashGet(hashId, key).ToString();
        }

        /// <summary>
        /// 獲取哈希值的全部鍵
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="hashId">哈希ID</param>
        /// <returns></returns>
        public static List<string> HashKeys(int dbIndex, string hashId)
        {
            var db = Instance.GetDatabase(dbIndex);
            var result = new List<string>();
            var list = db.HashKeys(hashId).ToList();
            if (list.Count > 0)
            {
                list.ForEach(x =>
                {
                    result.Add(ConvertObj<string>(x));
                });
            }
            return result;
        }

        /// <summary>
        /// 獲取全部哈希值
        /// </summary>
        /// <typeparam name="T">哈希值類型</typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="hashId">哈希ID</param>
        /// <returns></returns>
        public static List<T> HashGetAll<T>(int dbIndex, string hashId)
        {
            var db = Instance.GetDatabase(dbIndex);
            var result = new List<T>();
            var list = db.HashGetAll(hashId).ToList();
            if (list.Count > 0)
            {
                list.ForEach(x =>
                {
                    result.Add(ConvertObj<T>(x.Value));
                });
            }
            return result;
        }

        /// <summary>
        ///  刪除哈希值
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="hashId">哈希ID</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool HashDelete(int dbIndex, string hashId, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.HashDelete(hashId, key);
        }
        #endregion

        #region Lists
        /// <summary>
        /// 集合長度
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="listId">集合ID</param>
        /// <returns></returns>
        public static long ListLength(int dbIndex, string listId)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.ListLength(listId);
        }

        /// <summary>
        /// 向集合中添加元素
        /// </summary>
        /// <typeparam name="T">元素類型</typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="listId">集合ID</param>
        /// <param name="list">元素值</param>
        /// <returns></returns>
        public static long AddList<T>(int dbIndex, string listId, List<T> list)
        {
            var db = Instance.GetDatabase(dbIndex);
            if (list != null && list.Count > 0)
            {
                foreach (var item in list)
                {
                    db.ListRightPush(listId, ConvertJson(item));
                }
            }
            return db.ListLength(listId);
        }

        /// <summary>
        /// 獲取集合元素(默認獲取整個集合)
        /// </summary>
        /// <typeparam name="T">元素類型</typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="listId">集合ID</param>
        /// <param name="start">起始位置(0表示第1個位置)</param>
        /// <param name="stop">結束位置(-1表示倒數第1個位置)</param>
        /// <returns></returns>
        public static List<T> GetList<T>(int dbIndex, string listId, long start = 0, long stop = -1)
        {
            var db = Instance.GetDatabase(dbIndex);
            var result = new List<T>();
            var list = db.ListRange(listId, start, stop).ToList();
            if (list.Count > 0)
            {
                list.ForEach(x =>
                {
                    result.Add(ConvertObj<T>(x));
                });
            }
            return result;
        }
        #endregion

        #region ZSet

        #region 同步方法

        /// <summary>
        /// 添加一個值到Key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score">排序分數,爲空將獲取集合中最大score加1</param>
        /// <returns></returns>
        public static bool SortedSetAdd<T>(int dbIndex, string key, T value, double? score = null)
        {
            var db = Instance.GetDatabase(dbIndex);
            double scoreNum = score ?? _GetScore(key, db);
            return db.SortedSetAdd(key, ConvertJson<T>(value), scoreNum);
        }

        /// <summary>
        /// 添加一個集合到Key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score">排序分數,爲空將獲取集合中最大score加1</param>
        /// <returns></returns>
        public static long SortedSetAdd<T>(int dbIndex, string key, List<T> value, double? score = null)
        {
            var db = Instance.GetDatabase(dbIndex);
            double scoreNum = score ?? _GetScore(key, db);
            SortedSetEntry[] rValue = value.Select(o => new SortedSetEntry(ConvertJson<T>(o), scoreNum++)).ToArray();
            return db.SortedSetAdd(key, rValue);
        }

        /// <summary>
        /// 獲取集合中的數量
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static long SortedSetLength(int dbIndex,string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.SortedSetLength(key);
        }

        /// <summary>
        /// 獲取指定起始值到結束值的集合數量
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="startValue">起始值</param>
        /// <param name="endValue">結束值</param>
        /// <returns></returns>
        public static long SortedSetLengthByValue<T>(int dbIndex, string key, T startValue, T endValue)
        {
            var db = Instance.GetDatabase(dbIndex);
            var sValue = ConvertJson<T>(startValue);
            var eValue = ConvertJson<T>(endValue);
            return db.SortedSetLengthByValue(key, sValue, eValue);
        }

        /// <summary>
        /// 獲取指定Key的排序Score值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static double? SortedSetScore<T>(int dbIndex, string key, T value)
        {
            var db = Instance.GetDatabase(dbIndex);
            var rValue = ConvertJson<T>(value);
            return db.SortedSetScore(key, rValue);
        }

        /// <summary>
        /// 獲取指定Key中最小Score值
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static double SortedSetMinScore(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            double dValue = 0;
            var rValue = db.SortedSetRangeByRankWithScores(key, 0, 0, Order.Ascending).FirstOrDefault();
            dValue = rValue != null ? rValue.Score : 0;
            return dValue;
        }

        /// <summary>
        /// 獲取指定Key中最大Score值
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static double SortedSetMaxScore(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            double dValue = 0;
            var rValue = db.SortedSetRangeByRankWithScores(key, 0, 0, Order.Descending).FirstOrDefault();
            dValue = rValue != null ? rValue.Score : 0;
            return dValue;
        }

        /// <summary>
        /// 刪除Key中指定的值
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static long SortedSetRemove<T>(int dbIndex, string key, params T[] value)
        {
            var db = Instance.GetDatabase(dbIndex);
            var rValue = ConvertRedisValue<T>(value);
            return db.SortedSetRemove(key, rValue);
        }

        /// <summary>
        /// 刪除指定起始值到結束值的數據
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="startValue">起始值</param>
        /// <param name="endValue">結束值</param>
        /// <returns></returns>
        public static long SortedSetRemoveRangeByValue<T>(int dbIndex, string key, T startValue, T endValue)
        {
            var db = Instance.GetDatabase(dbIndex);
            var sValue = ConvertJson<T>(startValue);
            var eValue = ConvertJson<T>(endValue);
            return db.SortedSetRemoveRangeByValue(key, sValue, eValue);
        }

        /// <summary>
        /// 刪除 從 start 開始的 stop 條數據
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public static long SortedSetRemoveRangeByRank(int dbIndex, string key, long start, long stop)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.SortedSetRemoveRangeByRank(key, start, stop);
        }

        /// <summary>
        /// 根據排序分數Score,刪除從 start 開始的 stop 條數據
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public static long SortedSetRemoveRangeByScore(int dbIndex, string key, double start, double stop)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.SortedSetRemoveRangeByScore(key, start, stop);
        }

        /// <summary>
        /// 獲取從 start 開始的 stop 條數據
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="start">起始數</param>
        /// <param name="stop">-1表示到結束,0爲1條</param>
        /// <param name="desc">是否按降序排列</param>
        /// <returns></returns>
        public static List<T> SortedSetRangeByRank<T>(int dbIndex, string key, long start = 0, long stop = -1, bool desc = false)
        {
            var db = Instance.GetDatabase(dbIndex);
            Order orderBy = desc ? Order.Descending : Order.Ascending;
            var rValue = db.SortedSetRangeByRank(key, start, stop, orderBy);
            return ConvetList<T>(rValue);
        }
        #endregion

        #region 異步方法

        /// <summary>
        /// 添加一個值到Key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        ///  <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score">排序分數,爲空將獲取集合中最大score加1</param>
        /// <returns></returns>
        public static async Task<bool> SortedSetAddAsync<T>(int dbIndex, string key, T value, double? score = null)
        {
            var db = Instance.GetDatabase(dbIndex);
            double scoreNum = score ?? _GetScore(key, db);
            return await db.SortedSetAddAsync(key, ConvertJson<T>(value), scoreNum);
        }

        /// <summary>
        /// 添加一個集合到Key
        /// </summary>
        /// <typeparam name="T"></typeparam>
        ///  <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="score">排序分數,爲空將獲取集合中最大score加1</param>
        /// <returns></returns>
        public static async Task<long> SortedSetAddAsync<T>(int dbIndex, string key, List<T> value, double? score = null)
        {
            var db = Instance.GetDatabase(dbIndex);
            double scoreNum = score ?? _GetScore(key, db);
            SortedSetEntry[] rValue = value.Select(o => new SortedSetEntry(ConvertJson<T>(o), scoreNum++)).ToArray();
            return await db.SortedSetAddAsync(key, rValue);
        }

        /// <summary>
        /// 獲取集合中的數量
        /// </summary>
        ///  <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<long> SortedSetLengthAsync(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            return await db.SortedSetLengthAsync(key);
        }

        /// <summary>
        /// 獲取指定起始值到結束值的集合數量
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="startValue">起始值</param>
        /// <param name="endValue">結束值</param>
        /// <returns></returns>
        public static async Task<long> SortedSetLengthByValueAsync<T>(int dbIndex, string key, T startValue, T endValue)
        {
            var db = Instance.GetDatabase(dbIndex);
            var sValue = ConvertJson<T>(startValue);
            var eValue = ConvertJson<T>(endValue);
            return await db.SortedSetLengthByValueAsync(key, sValue, eValue);
        }

        /// <summary>
        /// 獲取指定Key中最大Score值
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<double> SortedSetMaxScoreAsync(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            double dValue = 0;
            var rValue = (await db.SortedSetRangeByRankWithScoresAsync(key, 0, 0, Order.Descending)).FirstOrDefault();
            dValue = rValue != null ? rValue.Score : 0;
            return dValue;
        }

        /// <summary>
        /// 刪除Key中指定的值
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static async Task<long> SortedSetRemoveAsync<T>(int dbIndex, string key, params T[] value)
        {
            var db = Instance.GetDatabase(dbIndex);
            var rValue = ConvertRedisValue<T>(value);
            return await db.SortedSetRemoveAsync(key, rValue);
        }

        /// <summary>
        /// 刪除指定起始值到結束值的數據
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="startValue">起始值</param>
        /// <param name="endValue">結束值</param>
        /// <returns></returns>
        public static async Task<long> SortedSetRemoveRangeByValueAsync<T>(int dbIndex, string key, T startValue, T endValue)
        {
            var db = Instance.GetDatabase(dbIndex);
            var sValue = ConvertJson<T>(startValue);
            var eValue = ConvertJson<T>(endValue);
            return await db.SortedSetRemoveRangeByValueAsync(key, sValue, eValue);
        }

        /// <summary>
        /// 刪除 從 start 開始的 stop 條數據
        /// </summary>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="start"></param>
        /// <param name="stop"></param>
        /// <returns></returns>
        public static async Task<long> SortedSetRemoveRangeByRankAsync(int dbIndex, string key, long start, long stop)
        {
            var db = Instance.GetDatabase(dbIndex);
            return await db.SortedSetRemoveRangeByRankAsync(key, start, stop);
        }

        #endregion

        #region private method
        /// <summary>
        /// 獲取幾個集合的交叉並集合,並保存到一個新Key中
        /// </summary>
        /// <param name="db"></param>
        /// <param name="operation">Union:並集  Intersect:交集  Difference:差集  詳見 <see cref="SetOperation"/></param>
        /// <param name="destination">保存的新Key名稱</param>
        /// <param name="keys">要操做的Key集合</param>
        /// <returns></returns>
        private static long _SortedSetCombineAndStore(IDatabase db, SetOperation operation, string destination, params string[] keys)
        {
            RedisKey[] keyList = ConvertRedisKeysAddSysCustomKey(keys);
            var rValue = db.SortedSetCombineAndStore(operation, destination, keyList);
            return rValue;

        }

        /// <summary>
        /// 將string類型的Key轉換成 <see cref="RedisKey"/> 型的Key,並添加前綴字符串
        /// </summary>
        /// <param name="redisKeys"></param>
        /// <returns></returns>
        private static RedisKey[] ConvertRedisKeysAddSysCustomKey(params string[] redisKeys) => redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();

        /// <summary>
        /// 獲取指定Key中最大Score值,
        /// </summary>
        /// <param name="key">key名稱,注意要先添加上Key前綴</param>
        /// <returns></returns>
        private static double _GetScore(string key, IDatabase db)
        {
            double dValue = 0;
            var rValue = db.SortedSetRangeByRankWithScores(key, 0, 0, Order.Descending).FirstOrDefault();
            dValue = rValue != null ? rValue.Score : 0;
            return dValue + 1;
        }
        #endregion

        #endregion

        #region SET

        /// <summary>
        ///   Add the specified member to the set stored at key. Specified members that are  already a member of this set are ignored. If key does not exist, a new set is created before adding the specified members.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool SetAdd<T>(int dbIndex, string key, T value)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.SetAdd(key, ConvertJson(value));
        }

        /// <summary>
        ///   Add the specified member to the set stored at key. Specified members that are  already a member of this set are ignored. If key does not exist, a new set is created before adding the specified members.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static async Task<bool> SetAddAsync<T>(int dbIndex, string key, T value)
        {
            var db = Instance.GetDatabase(dbIndex);
            return await db.SetAddAsync(key, ConvertJson(value));
        }

        /// <summary>
        /// Returns the members of the set resulting from the specified operation against the given sets.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex"></param>
        /// <param name="operation"></param>
        /// <param name="firstKey"></param>
        /// <param name="secondKey"></param>
        /// <returns></returns>
        public static string[] SetCombine(int dbIndex, SetOperation operation, string firstKey, string secondKey)
        {
            var db = Instance.GetDatabase(dbIndex);
            var array = db.SetCombine(operation, firstKey, secondKey); 
            return array.ToStringArray();
        }

        /// <summary>
        /// Returns the members of the set resulting from the specified operation against the given sets.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex"></param>
        /// <param name="operation"></param>
        /// <param name="firstKey"></param>
        /// <param name="secondKey"></param>
        /// <returns></returns>
        public static async Task<string[]> SetCombineAsync(int dbIndex, SetOperation operation, string firstKey, string secondKey)
        {
            var db = Instance.GetDatabase(dbIndex);
            var array = await db.SetCombineAsync(operation, firstKey, secondKey);
            return array.ToStringArray();
        }

        /// <summary>
        /// Returns if member is a member of the set stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns>Returns if member is a member of the set stored at key.</returns>
        public static bool SetContains<T>(int dbIndex, string key, T value)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.SetContains(key, ConvertJson(value));
        }

        /// <summary>
        /// Returns if member is a member of the set stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns>Returns if member is a member of the set stored at key.</returns>
        public static async Task<bool> SetContainsAsync<T>(int dbIndex, string key, T value)
        {
            var db = Instance.GetDatabase(dbIndex);
            return await db.SetContainsAsync(key, ConvertJson(value));
        }

        /// <summary>
        /// 返回對應鍵值集合的長度|Returns the set cardinality (number of elements) of the set stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static long SetLength(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.SetLength(key);
        }

        /// <summary>
        /// 返回對應鍵值集合的長度|Returns the set cardinality (number of elements) of the set stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<long> SetLengthAsync(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            return await db.SetLengthAsync(key);
        }

        /// <summary>
        /// 返回存儲在鍵的集合值的全部成員|Returns all the members of the set value stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static List<T> SetMembers<T>(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            var array = db.SetMembers(key);
            return ConvetList<T>(array);
        }

        /// <summary>
        /// 返回存儲在鍵的集合值的全部成員|Returns all the members of the set value stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<List<T>> SetMembersAsync<T>(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            var array = await db.SetMembersAsync(key);
            return ConvetList<T>(array);
        }

        /// <summary>
        /// Move member from the set at source to the set at destination. This operation is atomic. In every given moment the element will appear to be a member of source or destination for other clients. When the specified element already exists in the destination set, it is only removed from the source set.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="sourceKey"></param>
        /// <param name="destinationKey"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool SetMove<T>(int dbIndex, string sourceKey, string destinationKey, T value)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.SetMove(sourceKey, destinationKey, ConvertJson(value));
        }

        /// <summary>
        /// Move member from the set at source to the set at destination. This operation is atomic. In every given moment the element will appear to be a member of source or destination for other clients. When the specified element already exists in the destination set, it is only removed from the source set.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="sourceKey"></param>
        /// <param name="destinationKey"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static async Task<bool> SetMoveAsync<T>(int dbIndex, string sourceKey, string destinationKey, T value)
        {
            var db = Instance.GetDatabase(dbIndex);
            return await db.SetMoveAsync(sourceKey, destinationKey, ConvertJson(value));
        }

        /// <summary>
        /// Removes and returns a random element from the set value stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T SetPop<T>(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            var value = db.SetPop(key);
            return ConvertObj<T>(value);
        }

        /// <summary>
        /// Removes and returns a random element from the set value stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<T> SetPopAsync<T>(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            var value = await db.SetPopAsync(key);
            return ConvertObj<T>(value);
        }

        /// <summary>
        /// Return a random element from the set value stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T SetRandomMember<T>(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex);
            var value = db.SetRandomMember(key);
            return ConvertObj<T>(value);
        }

        /// <summary>
        /// Return a random element from the set value stored at key.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<T> SetRandomMemberAsync<T>(int dbIndex, string key)
        {
            var db = Instance.GetDatabase(dbIndex); 
            var value = await db.SetRandomMemberAsync(key);
            return ConvertObj<T>(value);
        }

        /// <summary>
        /// Return an array of count distinct elements if count is positive. If called with a negative count the behavior changes and the command is allowed to return the same element multiple times. In this case the numer of returned elements is the absolute value of the specified count.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string[] SetRandomMember(int dbIndex, string key, long count)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.SetRandomMembers(key, count).ToStringArray();
        }

        /// <summary>
        /// Return an array of count distinct elements if count is positive. If called with a negative count the behavior changes and the command is allowed to return the same element multiple times. In this case the numer of returned elements is the absolute value of the specified count.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static async Task<string[]> SetRandomMembersAsync(int dbIndex, string key, long count)
        {
            var db = Instance.GetDatabase(dbIndex);
            var array = await db.SetRandomMembersAsync(key, count);
            return array.ToStringArray();
        }

        /// <summary>
        /// Remove the specified member from the set stored at key. Specified members that  are not a member of this set are ignored.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static bool SetRemove(int dbIndex, string key, string value)
        {
            var db = Instance.GetDatabase(dbIndex);
            return db.SetRemove(key, value);
        }

        /// <summary>
        /// Remove the specified member from the set stored at key. Specified members that  are not a member of this set are ignored.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public static async Task<bool> SetRemoveAsync(int dbIndex, string key, string value)
        {
            var db = Instance.GetDatabase(dbIndex);
            return await db.SetRemoveAsync(key, value);
        }

        /// <summary>
        /// The SSCAN command is used to incrementally iterate over set; note: to resume an iteration via cursor, cast the original enumerable or enumerator to IScanningCursor.
        /// </summary>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static IEnumerable<RedisValue> SetScan(int dbIndex, string key)
        {
           return Instance.GetDatabase(dbIndex).SetScan(key);
        }
        #endregion

        #region  看成消息代理中間件使用 通常使用更專業的消息隊列來處理這種業務場景
        /// <summary>
        /// 看成消息代理中間件使用
        /// 消息組建中,重要的概念即是生產者,消費者,消息中間件。
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public static long Publish(string channel, string message)
        {
            ISubscriber sub = Instance.GetSubscriber();
            //return sub.Publish("messages", "hello");
            return sub.Publish(channel, message);
        }

        /// <summary>
        /// 在消費者端獲得該消息並輸出
        /// </summary>
        /// <param name="channelFrom"></param>
        /// <returns></returns>
        public static void Subscribe(string channelFrom)
        {
            ISubscriber sub = Instance.GetSubscriber();
            sub.Subscribe(channelFrom, (channel, message) =>
            {
                Console.WriteLine((string)message);
            });
        }
        #endregion

        #region EventHandler
        /// <summary>
        /// 鏈接失敗 , 若是從新鏈接成功你將不會收到這個通知
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e)
        {

        }

        /// <summary>
        /// 從新創建鏈接以前的錯誤
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e)
        {

        }

        /// <summary>
        /// 發生錯誤時
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerErrorMessage(object sender, RedisErrorEventArgs e)
        {
        }

        /// <summary>
        /// 更改集羣
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e)
        {
            // LogHelper.WriteInfoLog("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint);
        }

        /// <summary>
        /// redis類庫錯誤
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void MuxerInternalError(object sender, InternalErrorEventArgs e)
        {
        }
        #endregion

        #region 內部輔助方法

        /// <summary>
        /// 將對象轉換成string字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string ConvertJson<T>(T value)
        {
            string result = value is string ? value.ToString() :
                JsonConvert.SerializeObject(value, Formatting.None);
            return result;
        }
       
        /// <summary>
        /// 將值集合轉換成RedisValue集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="redisValues"></param>
        /// <returns></returns>
        private static RedisValue[] ConvertRedisValue<T>(params T[] redisValues) => redisValues.Select(o => (RedisValue)ConvertJson<T>(o)).ToArray();

        /// <summary>
        /// 將值反系列化成對象集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="values"></param>
        /// <returns></returns>
        public static List<T> ConvetList<T>(RedisValue[] values)
        {
            List<T> result = new List<T>();
            foreach (var item in values)
            {
                var model = ConvertObj<T>(item);
                result.Add(model);
            }
            return result;
        }

        /// <summary>
        /// 將值反系列化成對象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T ConvertObj<T>(RedisValue value)
        {
            return value.IsNullOrEmpty ? default(T) : JsonConvert.DeserializeObject<T>(value);
        }

       

        #endregion
    }
}
View Code

在以上RedisUtils幫助類的基礎上封裝一次調用:redis

 /// <summary>
    /// Redis幫助類
    /// </summary>
    public class RedisHelper
    {
        /// <summary>
        /// 緩存失效時長
        /// </summary>
        public const int EXPIRY = 30;

        private static int CheckDbIndex(int dbIndex)
        {
            if (dbIndex > 16 || dbIndex < 0)
            {
                dbIndex = 0;
            }
            return dbIndex;
        }

        /// <summary>
        /// 獲取緩存數據
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex">Redis數據庫索引</param>
        /// <param name="key">Redis鍵</param>
        /// <param name="fun">從其餘地方獲取數據源,並緩存到Redis中</param>
        /// <param name="timeout">過時時間,單位:分鐘</param>
        /// <returns></returns>
        public static T GetObject<T>(int dbIndex, string key, Func<T> fun, int? timeout = EXPIRY) where T : class
        {
            dbIndex = CheckDbIndex(dbIndex);
            T data = RedisUtils.StringGet<T>(dbIndex, key);
            if (data != null)
            {
                return data;
            }
            if (fun != null)
            {
                data = fun();
            }
            if (data != null)
            {
                TimeSpan? timeSp = null;
                if (timeout != null)
                    timeSp = TimeSpan.FromMinutes(Convert.ToDouble(timeout));
                RedisUtils.StringSet<T>(dbIndex, key, data, timeSp);
            }
            return data;
        }

        /// <summary>
        /// KV
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="func">如找不到則從func獲取</param>
        /// <param name="timeout">超時時間</param>
        /// <returns></returns>
        public static T GetObject_KV<T>(int dbIndex, string key, Func<T> func, TimeSpan? timeout) where T : class
        {
            T data = RedisUtils.StringGet<T>(dbIndex, key);
            if (data != null)
            {
                return data;
            }
            if (func != null)
            {
                data = func();
            }
            if (data != null)
            {
                RedisUtils.StringSet<T>(dbIndex, key, data, timeout);
            }
            return data;
        }

        /// <summary>
        /// 異步獲取緩存數據
        /// </summary>
        /// <typeparam name="T">數據集類型</typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="fun">從其餘地方獲取數據源,並緩存到Redis中</param>
        /// <param name="timeout">過時時間,單位:分鐘</param>
        /// <returns></returns>
        public static async Task<T> GetObjectAsync<T>(int dbIndex, string key, Func<T> fun, int timeout = EXPIRY) where T : class
        {
            dbIndex = CheckDbIndex(dbIndex);
            T data = RedisUtils.StringGet<T>(dbIndex, key);
            if (data != null)
            {
                return data;
            }

            if (fun != null)
            {
                data = await Task.Run(() =>
                {
                    return fun();
                });
            }
            if (data != null)
            {
                RedisUtils.StringSet<T>(dbIndex, key, data, TimeSpan.FromMinutes(timeout));
            }
            return data;
        }

        /// <summary>
        /// 異步獲取緩存數據
        /// </summary>
        /// <typeparam name="T">數據集類型</typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="fun">從其餘地方獲取數據源,並緩存到Redis中</param>
        /// <param name="timeout">過時時間,單位:分鐘</param>
        /// <returns></returns>
        public static async Task<T> GetObjectAsync<T>(int dbIndex, string key, Func<RedisCache<T>> fun, int timeout = EXPIRY) where T : class
        {
            dbIndex = CheckDbIndex(dbIndex);
            RedisCache<T> cache = new RedisCache<T>();
            cache.CacheData = RedisUtils.StringGet<T>(dbIndex, key);
            if (cache.CacheData != null)
            {
                return cache.CacheData;
            }

            var temp = await Task.Run(() =>
            {
                return fun();
            });

            if (temp != null) cache = temp;

            if (cache.UseCache)
            {
                RedisUtils.StringSet<T>(dbIndex, key, cache.CacheData, TimeSpan.FromMinutes(timeout));
            }
            return cache.CacheData;
        }

        /// <summary>
        /// 異步獲取數據集合
        /// </summary>
        /// <typeparam name="T">數據集類型</typeparam>
        /// <param name="dbIndex">數據庫</param>
        /// <param name="key"></param>
        /// <param name="fun">從其餘地方獲取數據源,並緩存到Redis中</param>
        /// <param name="timeout">過時時間,單位:分鐘</param>
        /// <returns></returns>
        public static async Task<List<T>> GetListAsync<T>(int dbIndex, string key, Func<List<T>> fun, int timeout = EXPIRY) where T : class
        {
            dbIndex = CheckDbIndex(dbIndex);
            List<T> datas = RedisUtils.StringGet<List<T>>(dbIndex, key);
            if (datas != null && datas.Count > 0)
            {
                return datas;
            }

            datas = await Task.Run(() =>
            {
                return fun();
            });

            if (datas != null && datas.Count > 0)
            {
                RedisUtils.StringSet<List<T>>(dbIndex, key, datas, TimeSpan.FromMinutes(timeout));
            }
            return datas;
        }

        /// <summary>
        /// ZSet
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex"></param>
        /// <param name="key"></param>
        /// <param name="func">如找不到則從func獲取</param>
        /// <returns></returns>
        public static List<T> GetObject_ZSet<T>(int dbIndex, string key, Func<List<T>> func) where T : class
        {
            List<T> data = RedisUtils.SortedSetRangeByRank<T>(dbIndex, key);
            if (data != null && data.Count > 0)
            {
                return data;
            }
            if (func != null)
            {
                data = func();
            }
            if (data != null)
            {
                RedisUtils.SortedSetAdd<T>(dbIndex, key, data);
            }
            return data;
        }


        /// <summary>
        /// Hash
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dbIndex"></param>
        /// <param name="hashID">hashID</param>
        /// <param name="key"></param>
        /// <param name="func">如找不到則從func獲取</param>
        /// <returns></returns>
        public static T GetObject_Hash<T>(int dbIndex, string hashID, string key, Func<T> func) where T : class
        {
            T data = RedisUtils.HashGet<T>(dbIndex, hashID, key);
            if (data != null)
            {
                return data;
            }
            if (func != null)
            {
                data = func();
            }
            if (data != null)
            {
                RedisUtils.HashSet<T>(dbIndex, hashID, key, data);
            }
            return data;
        }
    }

 修復

通過反覆閱讀源碼和測試,還有獲得源做者NickCraver的指導,ConnectionMultiplexer是線程安全的,在多線程的狀況下,避免使用lock來做爲單例來使用,出現的鏈接超時的狀況。數據庫

It was not possible to connect to the redis server(s).ConnectTimeout緩存

正確姿式在幫助類摺疊代碼裏面。安全

測試源碼:多線程

static void TestRedis()
        {
            int dbIdx = 4;
            string key = "testKey";
            Task.Run(()=> {
                while (true)
                {
                    Task.Run(() =>
                    {
                        RedisUtils.StringSet(dbIdx, key, DateTime.Now.ToString(), TimeSpan.FromSeconds(30));
                    });
                    Task.Run(() =>
                    {
                        string v = RedisUtils.StringGet(dbIdx, key);
                        Console.WriteLine("1:" + v);
                    });
                    System.Threading.Thread.Sleep(100);
                }
            });
            Task.Run(() => {
                while (true)
                {
                    Task.Run(() =>
                    {
                        RedisUtils.StringSet(dbIdx, key, DateTime.Now.ToString(), TimeSpan.FromSeconds(30));
                    });
                    Task.Run(() =>
                    {
                        string v = RedisUtils.StringGet(dbIdx, key);
                        Console.WriteLine("2:" + v);
                    });
                    System.Threading.Thread.Sleep(100);
                }
            });
            Task.Run(() => {
                while (true)
                {
                    Task.Run(() =>
                    {
                        RedisUtils.StringSet(dbIdx, key, DateTime.Now.ToString(), TimeSpan.FromSeconds(30));
                    });
                    Task.Run(() =>
                    {
                        string v = RedisUtils.StringGet(dbIdx, key);
                        Console.WriteLine("3:" + v);
                    });
                    System.Threading.Thread.Sleep(100);
                }
            });

        }
View Code

 已封裝在這裏👇app

Banana.Utility git 

相關文章
相關標籤/搜索