using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using MrWu.Debug; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using StackExchange.Redis; namespace Server.DB.Redis { public static class RedisCachingHelper { /// /// 默认缓存过期时间,单位:秒 /// public static int ExpireBySecond { get; set; } = 3600;// 86400; /* * * 主要用法 GetOrAdd * RedisType 参数表示 Redis 存储类型,当前支持,String/Hash/List * 泛型类型支持 * String 全部JSON支持的类型 * Hash 除简单类型外,所有有无参构造方法的类型, 注意:实现IDictionary的类型只支持IDictionary成员,不支持继承扩展属性/字段; * List 数组 以及 实现 IList 接口类型有无参构造的类型; * * acquire Redis 未命中时获取对象的委托方法, 必须返回一个有效对象,以避免缓存穿透 * key Redis 标识关键字 * dbIdx Redis Database Id * throwIfKeyTypeWrong 在从Redis获取对象时 Redis Key类型错误或对象成员类型错误时是否抛异常, 无此参数的重载默认为 true。 * 注意:所有操作redis的网络异常将被屏蔽,所有由类型不匹配和不支持类型将会抛 ArgumentOutOfRangeException 异常 * * 缓存过期时间指定,可以使用有expire参数的重载以固定值指定,也可以使用 acquire 参数类型为 Func> 的重载以灵活方式指定 * * 其他:使用本扩展方法,将会对象及对象成员的类型保持强类型特性,存储在redis上的数据是经过JSON格式包装,修改数据必须注意格式。 **/ #region 扩展方法 #region GetOrAdd Generic Sync public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire) { return dbStyle.GetOrAdd(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx) { return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, TimeSpan expire) { return dbStyle.GetOrAdd(redisType, key, acquire, -1, expire, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, int expireBySecond) { return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, TimeSpan expire) { return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, expire, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, TimeSpan expire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(redisType, key, acquire, -1, expire, throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = acquire(); db.AddToRedis(redisType, key, value, expire); return value; } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire) { return dbStyle.GetOrAdd(redisType, key, acquire, -1, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx) { return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(redisType, key, acquire, -1, throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = acquire(); db.AddToRedis(redisType, key, value.Item1, TimeSpan.FromSeconds(value.Item2)); return value.Item1; } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire) { return dbStyle.GetOrAdd(redisType, key, acquire, -1, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx) { return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(redisType, key, acquire, -1, throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = acquire(); db.AddToRedis(redisType, key, value.Item1, value.Item2); return value.Item1; } #endregion #region GetOrAdd Generic Async public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire) { return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx) { return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, TimeSpan expire) { return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, expire, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, int expireBySecond) { return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, TimeSpan expire) { return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, expire, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, TimeSpan expire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, expire, throwIfKeyTypeWrong); } public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func> acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = await acquire.Invoke(); db.AddToRedis(redisType, key, value, expire); return value; } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire) { return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, int dbIdx) { return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, throwIfKeyTypeWrong); } public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, int dbIdx, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = await acquire(); db.AddToRedis(redisType, key, value.Item1, TimeSpan.FromSeconds(value.Item2)); return value.Item1; } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire) { return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, int dbIdx) { return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, throwIfKeyTypeWrong); } public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func>> acquire, int dbIdx, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(redisType, key, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = await acquire(); db.AddToRedis(redisType, key, value.Item1, value.Item2); return value.Item1; } #endregion #region GetOrAdd RedisHash Sync public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire) { return dbStyle.GetOrAdd(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx) { return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, TimeSpan expire) { return dbStyle.GetOrAdd(key, field, acquire, -1, expire, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, int expireBySecond) { return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, TimeSpan expire) { return dbStyle.GetOrAdd(key, field, acquire, dbIdx, expire, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, TimeSpan expire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(key, field, acquire, -1, expire, throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = acquire(); db.AddToRedis(key, field, keyExists, value, expire); return value; } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire) { return dbStyle.GetOrAdd(key, field, acquire, -1, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx) { return dbStyle.GetOrAdd(key, field, acquire, dbIdx, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(key, field, acquire, -1, throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = acquire(); db.AddToRedis(key, field, keyExists, value.Item1, TimeSpan.FromSeconds(value.Item2)); return value.Item1; } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire) { return dbStyle.GetOrAdd(key, field, acquire, -1, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx) { return dbStyle.GetOrAdd(key, field, acquire, dbIdx, true); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(key, field, acquire, -1, throwIfKeyTypeWrong); } public static T GetOrAdd(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = acquire(); db.AddToRedis(key, field, keyExists, value.Item1, value.Item2); return value.Item1; } public static bool TryGet(this IDatabase db, RedisKey key, RedisValue field, out bool keyExists, out T outValue, bool throwIfKeyTypeWrong) { outValue = default(T); keyExists = false; try { var transaction = db.CreateTransaction(); var task1 = transaction.KeyTimeToLiveAsync(key); var task2 = transaction.HashGetAsync(key, field); if (transaction.Execute()) { var task1Result = task1.Result; keyExists = task1Result.HasValue && task1Result.Value > TimeSpan.Zero; var redisValue = task2.Result; if (!keyExists || redisValue.IsNullOrEmpty) { return false; } return GetObjectFromJsonWrapper(redisValue, out outValue, throwIfKeyTypeWrong, true, throwIfKeyTypeWrong); } } catch (Exception ex) { Console.WriteLine(ex); } return false; } public static bool AddToRedis(this IDatabase db, RedisKey key, RedisValue field, bool keyExists, object value, TimeSpan timeSpan) { try { var transaction = db.CreateTransaction(); transaction.HashSetAsync(key, field, GetStringOfJsonWrapper(value)); if (!keyExists) { transaction.KeyExpireAsync(key, timeSpan); } return transaction.Execute(); } catch (Exception ex) { Console.WriteLine(ex); } return false; } #endregion #region GetOrAdd RedisHash Async public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire) { return dbStyle.GetOrAddAsync(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx) { return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, TimeSpan expire) { return dbStyle.GetOrAddAsync(key, field, acquire, -1, expire, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, int expireBySecond) { return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, TimeSpan expire) { return dbStyle.GetOrAdd(key, field, acquire, dbIdx, expire, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, TimeSpan expire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(key, field, acquire, -1, expire, throwIfKeyTypeWrong); } public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func> acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = await acquire(); db.AddToRedis(key, field, keyExists, value, expire); return value; } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire) { return dbStyle.GetOrAddAsync(key, field, acquire, -1, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, int dbIdx) { return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(key, field, acquire, -1, throwIfKeyTypeWrong); } public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, int dbIdx, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = await acquire(); db.AddToRedis(key, field, keyExists, value.Item1, TimeSpan.FromSeconds(value.Item2)); return value.Item1; } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire) { return dbStyle.GetOrAddAsync(key, field, acquire, -1, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, int dbIdx) { return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, true); } public static Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, bool throwIfKeyTypeWrong) { return dbStyle.GetOrAddAsync(key, field, acquire, -1, throwIfKeyTypeWrong); } public static async Task GetOrAddAsync(this DbStyle dbStyle, RedisKey key, RedisValue field, Func>> acquire, int dbIdx, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); if (db.TryGet(key, field, out var keyExists, out var outValue, throwIfKeyTypeWrong)) { return outValue; } var value = await acquire(); db.AddToRedis(key, field, keyExists, value.Item1, value.Item2); return value.Item1; } #endregion #region UpdateOrAdd public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) { return dbStyle.UpdateOrAdd(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), true); } public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx) { return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); } public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) { return dbStyle.UpdateOrAdd(redisType, key, value, -1, expire, true); } public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, bool throwIfKeyTypeWrong) { return dbStyle.UpdateOrAdd(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, int expireBySecond) { return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); } public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, TimeSpan expire) { return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, expire, true); } public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, bool throwIfKeyTypeWrong) { return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { return dbStyle.UpdateOrAdd(redisType, key, value, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); } public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire, bool throwIfKeyTypeWrong) { return dbStyle.UpdateOrAdd(redisType, key, value, -1, expire, throwIfKeyTypeWrong); } public static T UpdateOrAdd(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); var existRedisKeyType = RedisType.None; try { //获取原有数据类型 existRedisKeyType = db.KeyType(key); } catch (Exception ex) { Debug.Error($"RedisCachingHelper UpdateOrAdd db.KeyType fail, key: {key}, ex: {ex}"); } //检查原有key数据类型 if (existRedisKeyType != RedisType.None && existRedisKeyType != redisType && throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"redis key 当前存储的数据类型为:{existRedisKeyType}, 新类型为:{redisType}"); } db.AddToRedis(redisType, key, value, expire); return value; } #endregion #region UpdateOrAdd Async public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) { return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), true); } public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx) { return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true); } public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) { return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, expire, true); } public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, bool throwIfKeyTypeWrong) { return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, int expireBySecond) { return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, TimeSpan.FromSeconds(expireBySecond), true); } public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, TimeSpan expire) { return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, expire, true); } public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, bool throwIfKeyTypeWrong) { return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong); } public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) { return dbStyle.UpdateOrAddAsync(redisType, key, value, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong); } public static Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire, bool throwIfKeyTypeWrong) { return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, expire, throwIfKeyTypeWrong); } public static async Task UpdateOrAddAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); var existRedisKeyType = RedisType.None; try { //获取原有数据类型 existRedisKeyType = await db.KeyTypeAsync(key); } catch (Exception ex) { Debug.Error($"RedisCachingHelper UpdateOrAdd db.KeyType fail, key: {key}, ex: {ex}"); } //检查原有key数据类型 if (existRedisKeyType != RedisType.None && existRedisKeyType != redisType && throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"redis key 当前存储的数据类型为:{existRedisKeyType}, 新类型为:{redisType}"); } db.AddToRedis(redisType, key, value, expire); return value; } #endregion #region Delete public static bool Delete(this DbStyle dbStyle, int dbIdx = -1, params RedisKey[] keys) { var db = redisManager.GetDataBase(dbStyle, dbIdx); return db.KeyDelete(keys) == keys.Length; } public static async Task DeleteAsync(this DbStyle dbStyle, int dbIdx = -1, params RedisKey[] keys) { var db = redisManager.GetDataBase(dbStyle, dbIdx); var count = await db.KeyDeleteAsync(keys); return count == keys.Length; } public static IDatabase GetDataBase(this DbStyle dbStyle, int dbIdx) { var db = redisManager.GetDataBase(dbStyle, dbIdx); return db; } #endregion #region TryGet public static bool TryGet(this DbStyle dbStyle, RedisType redisType, RedisKey key, out T outValue) { return dbStyle.TryGet(redisType, key, out outValue, -1, true); } public static bool TryGet(this DbStyle dbStyle, RedisType redisType, RedisKey key, out T outValue, int dbIdx) { return dbStyle.TryGet(redisType, key, out outValue, dbIdx, true); } public static bool TryGet(this DbStyle dbStyle, RedisType redisType, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { return dbStyle.TryGet(redisType, key, out outValue, -1, throwIfKeyTypeWrong); } public static bool TryGet(this DbStyle dbStyle, RedisType redisType, RedisKey key, out T outValue, int dbIdx, bool throwIfKeyTypeWrong) { var db = redisManager.GetDataBase(dbStyle, dbIdx); return db.TryGet(redisType, key, out outValue, throwIfKeyTypeWrong); } public static bool TryGet(this IDatabase db, RedisType redisType, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { EnsureKeyNotEmptyOrNULL(key); switch (redisType) { case RedisType.String: return db.TryGetString(key, out outValue, throwIfKeyTypeWrong); case RedisType.Hash: return db.TryGetHash(key, out outValue, throwIfKeyTypeWrong); case RedisType.List: return db.TryGetList(key, out outValue, throwIfKeyTypeWrong); case RedisType.Set: case RedisType.SortedSet: throw new NotSupportedException(); //return db.TryGetSet(key, out outValue, throwIfKeyTypeWrong); case RedisType.None: //case RedisType.Stream: case RedisType.Unknown: default: throw new NotSupportedException($"key: {key}, redisType: {redisType}"); } } #endregion #region AddToRedis public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) { return dbStyle.AddToRedis(redisType, key, value, TimeSpan.FromSeconds(ExpireBySecond), -1); } public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int expire) { return dbStyle.AddToRedis(redisType, key, value, TimeSpan.FromSeconds(expire), -1); } public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int expire, int dbIdx) { return dbStyle.AddToRedis(redisType, key, value, TimeSpan.FromSeconds(expire), dbIdx); } public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) { return dbStyle.AddToRedis(redisType, key, value, expire, -1); } public static bool AddToRedis(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire, int dbIdx) { var db = redisManager.GetDataBase(dbStyle, dbIdx); return db.AddToRedis(redisType, key, value, expire); } public static bool AddToRedis(this IDatabase db, RedisType redisType, RedisKey key, T value, TimeSpan expire) { EnsureKeyNotEmptyOrNULL(key); switch (redisType) { case RedisType.String: return db.AddStringToRedis(key, value, expire); case RedisType.Hash: return db.AddHashToRedis(key, value, expire); case RedisType.List: return db.AddListToRedis(key, value as IEnumerable, expire); case RedisType.Set: case RedisType.SortedSet: throw new NotSupportedException(); //return db.AddSetToRedis(key, value, expire); case RedisType.None: //case RedisType.Stream: case RedisType.Unknown: default: throw new NotSupportedException($"key: {key}, redisType: {redisType}"); } } #endregion #region AddToRedis Async public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) { return dbStyle.AddToRedisAsync(redisType, key, value, TimeSpan.FromSeconds(ExpireBySecond), -1); } public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int expire) { return dbStyle.AddToRedisAsync(redisType, key, value, TimeSpan.FromSeconds(expire), -1); } public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int expire, int dbIdx) { return dbStyle.AddToRedisAsync(redisType, key, value, TimeSpan.FromSeconds(expire), dbIdx); } public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) { return dbStyle.AddToRedisAsync(redisType, key, value, expire, -1); } public static Task AddToRedisAsync(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire, int dbIdx) { var db = redisManager.GetDataBase(dbStyle, dbIdx); return db.AddToRedisAsync(redisType, key, value, expire); } public static Task AddToRedisAsync(this IDatabase db, RedisType redisType, RedisKey key, T value, TimeSpan expire) { EnsureKeyNotEmptyOrNULL(key); switch (redisType) { case RedisType.String: return db.AddStringToRedisAsync(key, value, expire); case RedisType.Hash: return db.AddHashToRedisAsync(key, value, expire); case RedisType.List: return db.AddListToRedisAsync(key, value as IEnumerable, expire); case RedisType.Set: case RedisType.SortedSet: throw new NotSupportedException(); //return db.AddSetToRedis(key, value, expire); case RedisType.None: //case RedisType.Stream: case RedisType.Unknown: default: throw new NotSupportedException($"key: {key}, redisType: {redisType}"); } } #endregion public static IDatabase GetRedisDataBase(this DbStyle style, int dbIdx = -1) => redisManager.GetDataBase(style, dbIdx); #endregion #region 扩展功能实现 #region 以 Redis String 类型存储 static bool TryGetString(this IDatabase db, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { outValue = default(T); if (!db.GetFromRedis(key, throwIfKeyTypeWrong, p => p.StringGet(key), out var redisValue)) { return false; } //检查原始类型与目标类型是否一致 return !redisValue.IsNullOrEmpty && GetObjectFromJsonWrapper(redisValue, out outValue, throwIfKeyTypeWrong, true, throwIfKeyTypeWrong); } static bool AddStringToRedis(this IDatabase db, RedisKey key, T value, TimeSpan expire) { var stringValue = GetStringOfJsonWrapper(value); return db.ExecuctRedisTransaction(key, expire, false, transaction => transaction.StringSetAsync(key, stringValue)); } static Task AddStringToRedisAsync(this IDatabase db, RedisKey key, T value, TimeSpan expire) { var stringValue = GetStringOfJsonWrapper(value); return db.ExecuctRedisTransactionAsync(key, expire, false, transaction => transaction.StringSetAsync(key, stringValue)); } #endregion #region 以 Redis Hash 类型存储 public readonly static string hash_type_field_name = "_@_type_@_name_@_"; static bool TryGetHash(this IDatabase db, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { outValue = default(T); if (!db.GetFromRedis(key, throwIfKeyTypeWrong, p => p.HashGetAll(key), out var hashEntries)) { return false; } if (hashEntries.Length == 0) { return false; } var targetType = typeof(T); var item = hashEntries.SingleOrDefault(p => hash_type_field_name.Equals(p.Name)).Value; if (item.IsNullOrEmpty) { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"Hash 原始类型描述为空:目标类型:{ targetType.FullName}"); } return false; } Type originType = null; try { originType = Type.GetType(item); } catch (Exception ex) { Debug.Error($"TryGetHash Type.GetType fail, type: {item}, ex: {ex}"); } if (originType == null) { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无法获取原始类型:{originType}"); } return false; } hashEntries = hashEntries.Where(p => !string.Equals(p.Name, hash_type_field_name)).ToArray(); //检查原始类型与目标类型是否一致 if (!originType.Equals(targetType)) { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无效的目标类型:原始类型:{originType.FullName}, 目标类型:{targetType.FullName}"); } return false; } outValue = Activator.CreateInstance(); var dictionaryGeneric = targetType.GetInterface(typeOfIDictionaryGeneric.Name); if (dictionaryGeneric != null) { //目标类型是 IDictionary<,> var genericArgs = dictionaryGeneric.GetGenericArguments(); var method = TryGetIDictionaryGenericMethod.MakeGenericMethod(genericArgs); var succ = (bool)method.Invoke(null, new object[] { outValue, hashEntries }); if (!succ) { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"TryGetHash 解释元素类型不未成功!"); } } return succ; } else if (outValue is IDictionary dictionary) { //目标类型是 IDictionary return LoadDictionaryMembersFromHashEntries(hashEntries, dictionary, targetType, throwIfKeyTypeWrong); } else { //目标类型是非 IDictionary 类型 return LoadObjectMembersFromHashEntries(hashEntries, outValue, targetType, throwIfKeyTypeWrong); } } static readonly Type typeOfRedisCachingHelper = typeof(RedisCachingHelper); static Type typeOfObject { get; } = typeof(object); static Type typeOfCollectionGeneric { get; } = typeof(Collection<>); static Type typeOfIListGeneric { get; } = typeof(IList<>); static Type typeOfIList { get; } = typeof(IList); static Type typeOfIDictionaryGeneric { get; } = typeof(IDictionary<,>); static bool LoadDictionaryMembersFromHashEntries(HashEntry[] hashEntries, IDictionary dictionary, Type targetType, bool throwIfKeyTypeWrong) { foreach (var item in hashEntries) { if (!item.Name.IsNullOrEmpty) { try { if (GetObjectFromJsonWrapper(item.Name, out var dictionaryKeyname) && GetObjectFromJsonWrapper(item.Value, out var dictValue)) { dictionary.Add(dictionaryKeyname, dictValue); } } catch (Exception ex) { Debug.Error($"StaticCachingHelper LoadDictionaryMembersFromHashEntries fail, ex: {ex}"); if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"解析数据发生异常,目标类型:{targetType.FullName}!", ex); } return false; } } else { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无效的原始成员名称, Name:{item.Name}, Value:{targetType.FullName}"); } return false; } } return true; } static bool LoadObjectMembersFromHashEntries(HashEntry[] hashEntries, object instance, Type targetType, bool throwIfKeyTypeWrong) { var fields = targetType.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(p => !p.IsInitOnly).ToArray(); var props = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0).ToArray(); if (fields.Length == 0 && props.Length == 0) { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"类型:{targetType} 没有公开的可读写字段或属性"); } return false; } foreach (var item in hashEntries) { if (!item.Value.IsNullOrEmpty) { Action valueSetAction = null; Type memberType = null; var fieldInfo = fields.SingleOrDefault(p => p.Name == item.Name); if (fieldInfo != null) { valueSetAction = fieldInfo.SetValue; memberType = fieldInfo.FieldType; } else { var propInfo = props.SingleOrDefault(p => p.Name == item.Name); if (propInfo != null) { valueSetAction = propInfo.SetValue; memberType = propInfo.PropertyType; } else { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无效的原始成员名称, Name:{item.Name}, Value:{item.Value}"); } //缓存的数据对象成员与当前的类型的成员不一致,放弃当前缓存数据 return false; } } if (GetObjectFromJsonWrapper(item.Value, out var memberValue, out var memberOriginType, throwIfKeyTypeWrong, false) && memberType.IsAssignableFrom(memberOriginType)) { valueSetAction.Invoke(instance, memberValue); } else { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无效的原始成员类型与当前成员类型匹配,当前成员类型:{item}, 原始成员类型:{memberOriginType}"); } return false; } } else { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无效的原始数据, Name:{item.Name}, Value:{item.Value}"); } //缓存的数据对象成员与当前的类型的成员不一致,放弃当前缓存数据 return false; } } return true; } static MethodInfo TryGetIDictionaryGenericMethod { get; } = typeOfRedisCachingHelper.GetMethod(nameof(TryGetIDictionaryGeneric), BindingFlags.Static | BindingFlags.NonPublic); static bool TryGetIDictionaryGeneric(IDictionary dictionary, HashEntry[] hashEntries) { var keyType = typeof(TKey); var valueType = typeof(TValue); foreach (var hashEntry in hashEntries) { if (hashEntry.Name.IsNullOrEmpty || hashEntry.Value.IsNullOrEmpty) { return false; } else if (GetObjectFromJsonWrapper(hashEntry.Name, out var key, out var originKeyType, false, false) && GetObjectFromJsonWrapper(hashEntry.Value, out var value, out var originValueType, false, false)) { if (!(keyType.IsAssignableFrom(originKeyType) || (key == null && (keyType.IsClass || keyType.IsInterface)))) {//反序列化成员数据失败,有可能成员类型已改变 Debug.Error($"StaticCachingHelper TryGetIDictionaryGeneric fail, hashEntry.Name: {hashEntry.Name}, 类型不正确: 要求元素类型:{keyType}, 原始数据元素类型:{originKeyType}"); return false; } if (!(valueType.IsAssignableFrom(originValueType) || (value == null && (valueType.IsClass || valueType.IsInterface)))) { Debug.Error($"StaticCachingHelper TryGetIDictionaryGeneric fail, hashEntry.Value: {hashEntry.Value}, 类型不正确: 要求元素类型:{valueType}, 原始数据元素类型:{originValueType}"); return false; } dictionary.Add((TKey)key, (TValue)value);//反序列化成员数据失败,有可能成员类型已改变 } else { return false; } } return true; } static bool AddHashToRedis(this IDatabase db, RedisKey key, T value, TimeSpan expire) { if (value == null) { throw new ArgumentNullException("value is null!"); } var type = value.GetType(); //为了保持缓存对象强类型特性,将泛型类型一并写入,以便取出时检查 var entrys = new Collection(); entrys.Add(new HashEntry(hash_type_field_name, type.AssemblyQualifiedName)); var dictionary = value as IDictionary; if (dictionary != null) { if (dictionary.IsReadOnly) { throw new ArgumentOutOfRangeException("无法使用只读类型对象!"); } //IDictionary 类型 foreach (DictionaryEntry item in dictionary) { entrys.Add(new HashEntry(GetStringOfJsonWrapper(item.Key), GetStringOfJsonWrapper(item.Value))); } } else { //class 或 struct var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(p => !p.IsInitOnly).ToArray(); var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0).ToArray(); if (fields.Length == 0 && props.Length == 0) { throw new ArgumentOutOfRangeException("Redis Hash 不支持简单类型和无成员类型, 必须有公开的可读写内部成员!"); } foreach (var fieldInfo in fields) { var name = fieldInfo.Name; var fieldValue = fieldInfo.GetValue(value); entrys.Add(new HashEntry(name, GetStringOfJsonWrapper(fieldValue))); } foreach (var propInfo in props) { var name = propInfo.Name; var propValue = propInfo.GetValue(value); entrys.Add(new HashEntry(name, GetStringOfJsonWrapper(propValue))); } } return db.ExecuctRedisTransaction(key, expire, true, transaction => transaction.HashSetAsync(key, entrys.ToArray())); } static Task AddHashToRedisAsync(this IDatabase db, RedisKey key, T value, TimeSpan expire) { if (value == null) { throw new ArgumentNullException("value is null!"); } var type = value.GetType(); //为了保持缓存对象强类型特性,将泛型类型一并写入,以便取出时检查 var entrys = new Collection(); entrys.Add(new HashEntry(hash_type_field_name, type.AssemblyQualifiedName)); var dictionary = value as IDictionary; if (dictionary != null) { if (dictionary.IsReadOnly) { throw new ArgumentOutOfRangeException("无法使用只读类型对象!"); } //IDictionary 类型 foreach (DictionaryEntry item in dictionary) { entrys.Add(new HashEntry(GetStringOfJsonWrapper(item.Key), GetStringOfJsonWrapper(item.Value))); } } else { //class 或 struct var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(p => !p.IsInitOnly).ToArray(); var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0).ToArray(); if (fields.Length == 0 && props.Length == 0) { throw new ArgumentOutOfRangeException("Redis Hash 不支持简单类型和无成员类型, 必须有公开的可读写内部成员!"); } foreach (var fieldInfo in fields) { var name = fieldInfo.Name; var fieldValue = fieldInfo.GetValue(value); entrys.Add(new HashEntry(name, GetStringOfJsonWrapper(fieldValue))); } foreach (var propInfo in props) { var name = propInfo.Name; var propValue = propInfo.GetValue(value); entrys.Add(new HashEntry(name, GetStringOfJsonWrapper(propValue))); } } return db.ExecuctRedisTransactionAsync(key, expire, true, transaction => { transaction.HashSetAsync(key, entrys.ToArray()); return Task.FromResult(true); }); } #endregion #region 以 Redis List 类型存储 static bool TryGetList(this IDatabase db, RedisKey key, out T outValue, bool throwIfKeyTypeWrong) { outValue = default(T); if (!db.GetFromRedis(key, throwIfKeyTypeWrong, p => p.ListRange(key), out var redisValues)) { return false; } if (redisValues.Length == 0) { return false; } //获取第一个元素 var typeString = redisValues.First(); if (typeString.IsNullOrEmpty) { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无效的目标类型:typeString is empty"); } return false; } Type originType = null; try { //获取原始数据类型 originType = Type.GetType(typeString); } catch (Exception ex) { Debug.Error($"TryGetList Type.GetType fail, type: {typeString}, ex: {ex}"); } if (originType == null) { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无法获取原始类型:{originType}"); } return false; } redisValues = redisValues.Skip(1).ToArray(); var targetType = typeof(T); if (!originType.Equals(targetType)) { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无效的目标类型:原始类型:{originType.FullName}, 目标类型:{targetType.FullName}"); } return false; } //检查原始类型与目标类型是否一致 var listInterface = targetType.GetInterface(typeOfIListGeneric.Name); if (listInterface != null) { var genericArgs = listInterface.GenericTypeArguments[0]; //如果目标类型是数组,先准备一个通用集合接收数组成员, var collectionType = targetType.IsArray ? typeOfCollectionGeneric.MakeGenericType(genericArgs) : targetType; //创建目标对象或接收集合 var list = Activator.CreateInstance(collectionType); //创建泛型方法 var method = TryGetListGenericMethodInfo.MakeGenericMethod(collectionType, genericArgs); //执行泛型方法 var succ = (bool)method.Invoke(null, new object[] { list, redisValues }); if (!succ) { if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"TryGetList 解释成员类型不未成功!"); } return false; } //处理目标类型或接收集合结果 outValue = !targetType.IsArray ? (T)list : (T)ListToArrayMethodInfo.MakeGenericMethod(genericArgs).Invoke(null, new[] { list }); return succ; } else { outValue = Activator.CreateInstance(); if (outValue is IList list) { return TryGetList(list, redisValues, throwIfKeyTypeWrong); } if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无效的目标类型:原始类型:{originType.FullName}, 目标类型:{targetType.FullName}, 目标类型必须实现 {typeOfIList.FullName} 或 {typeOfIListGeneric.FullName}"); } } return false; } static MethodInfo ListToArrayMethodInfo { get; } = typeOfRedisCachingHelper.GetMethod(nameof(ListToArray), BindingFlags.Static | BindingFlags.NonPublic); static T[] ListToArray(IList list) { return list.ToArray(); } static MethodInfo TryGetListGenericMethodInfo { get; } = typeOfRedisCachingHelper.GetMethod(nameof(TryGetListGeneric), BindingFlags.Static | BindingFlags.NonPublic); static bool TryGetListGeneric(TCollection list, RedisValue[] redisValues) where TCollection : IList { var elementType = typeof(TElement); foreach (var redisValue in redisValues) { if (!redisValue.IsNullOrEmpty) { if (GetObjectFromJsonWrapper(redisValue, out var value, out var originelementType, false, false)) { if (elementType.IsAssignableFrom(originelementType) || (value == null && (elementType.IsClass || elementType.IsInterface))) {//反序列化成员数据失败,有可能成员类型已改变 list.Add((TElement)value); continue; } else { Debug.Error($"StaticCachingHelper TryGetListGeneric fail, redisValue: {redisValue}, 元素类型不正确: 要求元素类型:{elementType}, 数据元素类型:{originelementType}"); return false; } } } return false; } return true; } static bool TryGetList(IList list, RedisValue[] redisValues, bool throwIfKeyTypeWrong) { foreach (var redisValue in redisValues) { if (GetObjectFromJsonWrapper(redisValue, out var value, throwIfKeyTypeWrong, throwIfKeyTypeWrong)) { list.Add(value); } else { return false; } } return true; } static bool AddListToRedis(this IDatabase db, RedisKey key, T obj, TimeSpan expire) where T : IEnumerable { if (obj == null) { throw new ArgumentOutOfRangeException("list is null or not is IList or IList<> type!"); } if (!(obj is IList list)) { throw new ArgumentOutOfRangeException("list is null or not is IList or IList<> type!"); } var valueList = new Collection(); valueList.Add(list.GetType().AssemblyQualifiedName); foreach (var item in list) { valueList.Add(GetStringOfJsonWrapper(item)); } return db.ExecuctRedisTransaction(key, expire, true, transaction => transaction.ListRightPushAsync(key, valueList.ToArray())); } static Task AddListToRedisAsync(this IDatabase db, RedisKey key, T obj, TimeSpan expire) where T : IEnumerable { if (obj == null) { throw new ArgumentOutOfRangeException("list is null or not is IList or IList<> type!"); } if (!(obj is IList list)) { throw new ArgumentOutOfRangeException("list is null or not is IList or IList<> type!"); } var valueList = new Collection(); valueList.Add(list.GetType().AssemblyQualifiedName); foreach (var item in list) { valueList.Add(GetStringOfJsonWrapper(item)); } return db.ExecuctRedisTransactionAsync(key, expire, true, async transaction => (await transaction.ListRightPushAsync(key, valueList.ToArray())) > 0); } #endregion #region 以 Redis Set 类型存储 #endregion #region JSON 数据包装 public static string GetStringOfJsonWrapper(object value) { return JToken.FromObject(new { type = (value == null ? typeOfObject : value.GetType()).AssemblyQualifiedName, value }).ToString(Formatting.None); } static bool GetObjectFromJsonWrapper(string jsonString, out object value, bool throwIfEmpty = true, bool throwIfJsonStringWrong = true) { if (GetObjectFromJsonWrapper(jsonString, out value, out var t, throwIfEmpty, throwIfJsonStringWrong)) { return true; } return false; } static bool GetObjectFromJsonWrapper(string jsonString, out T value, bool throwIfKeyTypeWrong, bool throwIfEmpty, bool throwIfJsonStringWrong) { if (GetObjectFromJsonWrapper(jsonString, out var obj, out var originelementType, throwIfEmpty, throwIfJsonStringWrong)) { var targetType = typeof(T); if (targetType.IsAssignableFrom(originelementType)) { value = (T)obj; return true; } else if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"无效的目标类型:原始数据类型:{originelementType.FullName},目标类型:{targetType.FullName}"); } } value = default(T); return false; } static bool GetObjectFromJsonWrapper(string jsonString, out object value, out Type type, bool throwIfEmpty, bool throwIfJsonStringWrong) { value = null; type = null; if (string.IsNullOrEmpty(jsonString)) { if (throwIfEmpty) { throw new ArgumentOutOfRangeException("GetObjectFromJsonWrapper jsonString is empty or null!"); } return false; } try { var json = JObject.Parse(jsonString); if (json.TryGetValue("type", out var jType) && json.TryGetValue("value", out var jvalue)) { type = Type.GetType(jType.ToObject()); if (type != null) { value = jvalue.ToObject(type); return true; } } } catch (Exception ex) { Debug.Error("GetObjectFromJsonWrapper fail, ex: " + ex); } if (throwIfJsonStringWrong) { throw new ArgumentOutOfRangeException("GetObjectFromJsonWrapper jsonString: " + jsonString); } return false; } #endregion #region common static void EnsureKeyNotEmptyOrNULL(RedisKey key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentOutOfRangeException("key is null or empty!"); } } static bool GetFromRedis(this IDatabase db, RedisKey key, bool throwIfKeyTypeWrong, Func acquire, out T value) { value = default(T); try { //获取List对象 value = acquire(db); return true; } catch (RedisServerException rse) { Debug.Error($"StaticCachingHelper GetFromRedis fail, key: {key}, rse: {rse}"); if (throwIfKeyTypeWrong) { throw new ArgumentOutOfRangeException($"redis key type wrong, key: {key}", rse); } } catch (Exception ex) { Debug.Error($"StaticCachingHelper GetFromRedis fail, key: {key}, ex: {ex}"); } return false; } static bool ExecuctRedisTransaction(this IDatabase db, RedisKey key, TimeSpan expire, bool deleteKey, Action action) { try { var transaction = db.CreateTransaction(); if (deleteKey) { transaction.KeyDeleteAsync(key); } //为了保持缓存对象强类型特性,将泛型类型一并写入,以便取出时检查 action.Invoke(transaction); if (expire > TimeSpan.Zero) { transaction.KeyExpireAsync(key, expire); } var succ = transaction.Execute(); if (!succ) { Debug.Error($"StaticCachingHelper ExecuctRedisTransaction fail 1, key: {key}, expire: {expire}"); } return succ; } catch (Exception ex) { Debug.Error($"StaticCachingHelper ExecuctRedisTransaction fail 2, key: {key}, expire: {expire}, ex: {ex}"); } return false; } static async Task ExecuctRedisTransactionAsync(this IDatabase db, RedisKey key, TimeSpan expire, bool deleteKey, Func> action) { try { var transaction = db.CreateTransaction(); var deleteOldTask = deleteKey ? transaction.KeyDeleteAsync(key) : null; //为了保持缓存对象强类型特性,将泛型类型一并写入,以便取出时检查 var result = action.Invoke(transaction); var setExpireTask = (expire > TimeSpan.Zero) ? transaction.KeyExpireAsync(key, expire) : null; var succ = transaction.Execute(); if (!succ) { Debug.Error($"StaticCachingHelper ExecuctRedisTransaction fail 1, key: {key}, expire: {expire}"); } if (deleteOldTask != null) { await deleteOldTask; } if (setExpireTask != null) { await setExpireTask; } return succ && await result; } catch (Exception ex) { Debug.Error($"StaticCachingHelper ExecuctRedisTransaction fail 2, key: {key}, expire: {expire}, ex: {ex}"); } return false; } #endregion #endregion } }