Files
hjha-server/GameDAL/db/RedisCachingHelper.cs
xiaoou e9616125ce feat: initial commit - HJHA game server full source
6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) +
game logic (GameFix/GameDAL/ServerData) +
network layer (NetWorkMessage) +
data layer (ObjectModel) +
utilities (MrWu/Core/Config/CloudAPI/dll) +
adapters (zyxAdapter/base)

.NET 8.0 C# solution, 16 projects, 958 source files
2026-07-07 12:02:15 +08:00

1200 lines
68 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {
/// <summary>
/// 默认缓存过期时间,单位:秒
/// </summary>
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<Tuple<T, int>> 的重载以灵活方式指定
*
* 其他使用本扩展方法将会对象及对象成员的类型保持强类型特性存储在redis上的数据是经过JSON格式包装修改数据必须注意格式。
**/
#region
#region GetOrAdd Generic Sync
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire) {
return dbStyle.GetOrAdd(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire, int dbIdx) {
return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire, TimeSpan expire) {
return dbStyle.GetOrAdd(redisType, key, acquire, -1, expire, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire, int dbIdx, int expireBySecond) {
return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire, int dbIdx, TimeSpan expire) {
return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, expire, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire, TimeSpan expire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(redisType, key, acquire, -1, expire, throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<T> acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(redisType, key, out var outValue, throwIfKeyTypeWrong)) {
return outValue;
}
var value = acquire();
db.AddToRedis(redisType, key, value, expire);
return value;
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Tuple<T, int>> acquire) {
return dbStyle.GetOrAdd(redisType, key, acquire, -1, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Tuple<T, int>> acquire, int dbIdx) {
return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Tuple<T, int>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(redisType, key, acquire, -1, throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Tuple<T, int>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Tuple<T, TimeSpan>> acquire) {
return dbStyle.GetOrAdd(redisType, key, acquire, -1, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Tuple<T, TimeSpan>> acquire, int dbIdx) {
return dbStyle.GetOrAdd(redisType, key, acquire, dbIdx, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Tuple<T, TimeSpan>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(redisType, key, acquire, -1, throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Tuple<T, TimeSpan>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire, int dbIdx) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire, TimeSpan expire) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, expire, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire, int dbIdx, int expireBySecond) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire, int dbIdx, TimeSpan expire) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, expire, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire, TimeSpan expire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, expire, throwIfKeyTypeWrong);
}
public static async Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<T>> acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(redisType, key, out var outValue, throwIfKeyTypeWrong)) {
return outValue;
}
var value = await acquire.Invoke();
db.AddToRedis(redisType, key, value, expire);
return value;
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<Tuple<T, int>>> acquire) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<Tuple<T, int>>> acquire, int dbIdx) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<Tuple<T, int>>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, throwIfKeyTypeWrong);
}
public static async Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<Tuple<T, int>>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<Tuple<T, TimeSpan>>> acquire) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<Tuple<T, TimeSpan>>> acquire, int dbIdx) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, dbIdx, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<Tuple<T, TimeSpan>>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(redisType, key, acquire, -1, throwIfKeyTypeWrong);
}
public static async Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, Func<Task<Tuple<T, TimeSpan>>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire) {
return dbStyle.GetOrAdd(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire, int dbIdx) {
return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire, TimeSpan expire) {
return dbStyle.GetOrAdd(key, field, acquire, -1, expire, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire, int dbIdx, int expireBySecond) {
return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire, int dbIdx, TimeSpan expire) {
return dbStyle.GetOrAdd(key, field, acquire, dbIdx, expire, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire, TimeSpan expire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(key, field, acquire, -1, expire, throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<T> acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Tuple<T, int>> acquire) {
return dbStyle.GetOrAdd(key, field, acquire, -1, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Tuple<T, int>> acquire, int dbIdx) {
return dbStyle.GetOrAdd(key, field, acquire, dbIdx, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Tuple<T, int>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(key, field, acquire, -1, throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Tuple<T, int>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Tuple<T, TimeSpan>> acquire) {
return dbStyle.GetOrAdd(key, field, acquire, -1, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Tuple<T, TimeSpan>> acquire, int dbIdx) {
return dbStyle.GetOrAdd(key, field, acquire, dbIdx, true);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Tuple<T, TimeSpan>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(key, field, acquire, -1, throwIfKeyTypeWrong);
}
public static T GetOrAdd<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Tuple<T, TimeSpan>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T>(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<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire) {
return dbStyle.GetOrAddAsync(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire, int dbIdx) {
return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire, TimeSpan expire) {
return dbStyle.GetOrAddAsync(key, field, acquire, -1, expire, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(key, field, acquire, -1, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire, int dbIdx, int expireBySecond) {
return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire, int dbIdx, TimeSpan expire) {
return dbStyle.GetOrAdd(key, field, acquire, dbIdx, expire, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, TimeSpan.FromSeconds(ExpireBySecond), throwIfKeyTypeWrong);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire, int dbIdx, int expireBySecond, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAdd(key, field, acquire, dbIdx, TimeSpan.FromSeconds(expireBySecond), throwIfKeyTypeWrong);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire, TimeSpan expire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(key, field, acquire, -1, expire, throwIfKeyTypeWrong);
}
public static async Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<T>> acquire, int dbIdx, TimeSpan expire, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<Tuple<T, int>>> acquire) {
return dbStyle.GetOrAddAsync(key, field, acquire, -1, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<Tuple<T, int>>> acquire, int dbIdx) {
return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<Tuple<T, int>>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(key, field, acquire, -1, throwIfKeyTypeWrong);
}
public static async Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<Tuple<T, int>>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<Tuple<T, TimeSpan>>> acquire) {
return dbStyle.GetOrAddAsync(key, field, acquire, -1, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<Tuple<T, TimeSpan>>> acquire, int dbIdx) {
return dbStyle.GetOrAddAsync(key, field, acquire, dbIdx, true);
}
public static Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<Tuple<T, TimeSpan>>> acquire, bool throwIfKeyTypeWrong) {
return dbStyle.GetOrAddAsync(key, field, acquire, -1, throwIfKeyTypeWrong);
}
public static async Task<T> GetOrAddAsync<T>(this DbStyle dbStyle, RedisKey key, RedisValue field, Func<Task<Tuple<T, TimeSpan>>> acquire, int dbIdx, bool throwIfKeyTypeWrong) {
var db = redisManager.GetDataBase(dbStyle, dbIdx);
if (db.TryGet<T>(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<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) {
return dbStyle.UpdateOrAdd(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static T UpdateOrAdd<T>(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<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) {
return dbStyle.UpdateOrAdd(redisType, key, value, -1, expire, true);
}
public static T UpdateOrAdd<T>(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<T>(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<T>(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<T>(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<T>(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<T>(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<T>(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<T> UpdateOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) {
return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, TimeSpan.FromSeconds(ExpireBySecond), true);
}
public static Task<T> UpdateOrAddAsync<T>(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<T> UpdateOrAddAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) {
return dbStyle.UpdateOrAddAsync(redisType, key, value, -1, expire, true);
}
public static Task<T> UpdateOrAddAsync<T>(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<T> UpdateOrAddAsync<T>(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<T> UpdateOrAddAsync<T>(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<T> UpdateOrAddAsync<T>(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<T> UpdateOrAddAsync<T>(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<T> UpdateOrAddAsync<T>(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<T> UpdateOrAddAsync<T>(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<bool> 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<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, out T outValue) {
return dbStyle.TryGet(redisType, key, out outValue, -1, true);
}
public static bool TryGet<T>(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<T>(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<T>(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<T>(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<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) {
return dbStyle.AddToRedis(redisType, key, value, TimeSpan.FromSeconds(ExpireBySecond), -1);
}
public static bool AddToRedis<T>(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<T>(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<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) {
return dbStyle.AddToRedis(redisType, key, value, expire, -1);
}
public static bool AddToRedis<T>(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<T>(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<bool> AddToRedisAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value) {
return dbStyle.AddToRedisAsync(redisType, key, value, TimeSpan.FromSeconds(ExpireBySecond), -1);
}
public static Task<bool> AddToRedisAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, int expire) {
return dbStyle.AddToRedisAsync(redisType, key, value, TimeSpan.FromSeconds(expire), -1);
}
public static Task<bool> AddToRedisAsync<T>(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<bool> AddToRedisAsync<T>(this DbStyle dbStyle, RedisType redisType, RedisKey key, T value, TimeSpan expire) {
return dbStyle.AddToRedisAsync(redisType, key, value, expire, -1);
}
public static Task<bool> AddToRedisAsync<T>(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<bool> AddToRedisAsync<T>(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<T>(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<T>(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<bool> AddStringToRedisAsync<T>(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<T>(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<T>();
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<object, object> 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<TKey, TValue>(IDictionary<TKey, TValue> 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<T>(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<HashEntry>();
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<bool> AddHashToRedisAsync<T>(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<HashEntry>();
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<T>(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<T>();
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<T>(IList<T> list) {
return list.ToArray();
}
static MethodInfo TryGetListGenericMethodInfo { get; } = typeOfRedisCachingHelper.GetMethod(nameof(TryGetListGeneric), BindingFlags.Static | BindingFlags.NonPublic);
static bool TryGetListGeneric<TCollection, TElement>(TCollection list, RedisValue[] redisValues) where TCollection : IList<TElement> {
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<T>(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<RedisValue>();
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<bool> AddListToRedisAsync<T>(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<RedisValue>();
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<T>(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<string>());
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<T>(this IDatabase db, RedisKey key, bool throwIfKeyTypeWrong, Func<IDatabase, T> 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<ITransaction> 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<bool> ExecuctRedisTransactionAsync(this IDatabase db, RedisKey key, TimeSpan expire, bool deleteKey, Func<ITransaction, Task<bool>> 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
}
}