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
    }
}
