zoukankan      html  css  js  c++  java
  • .Net Core3.0 WebApi 项目框架搭建 十五:基于AOP的切面redis缓存

    什么是AOP

    www.baidu.com

    更新Redis缓存接口

    在之前的redis缓存博客中我们定义了redis操作接口和实现,在实际项目开发中,我又对它进行了修改,主要是增加了异步和批量删除的接口。修改Common下的Redis文件夹的IRedisCacheManager文件和RedisCacheManager增加以下接口和方法。

    IRedisCacheManager:

            //获取 Reids 缓存值
            Task<string> GetValueAsync(string key);
    
            //获取值,并序列化
            Task<TEntity> GetAsync<TEntity>(string key);
    
            //保存
            Task SetAsync(string key, object value, TimeSpan cacheTime);
    
            //判断是否存在
            Task<bool> GetAsync(string key);
    
            //移除某一个缓存值
            Task RemoveAsync(string key);
    
            //根据关键字移除
            Task RemoveByKey(string key);
    
            //全部清除
            Task ClearAsync();

    RedisCacheManager:

            public async Task ClearAsync()
            {
                foreach (var endPoint in this.GetRedisConnection().GetEndPoints())
                {
                    var server = this.GetRedisConnection().GetServer(endPoint);
                    foreach (var key in server.Keys())
                    {
                        await redisConnection.GetDatabase().KeyDeleteAsync(key);
                    }
                }
            }
    
            public async Task<bool> GetAsync(string key)
            {
                return await redisConnection.GetDatabase().KeyExistsAsync(key);
            }
    
            public async Task<string> GetValueAsync(string key)
            {
                return await redisConnection.GetDatabase().StringGetAsync(key);
            }
    
            public async Task<TEntity> GetAsync<TEntity>(string key)
            {
                var value = await redisConnection.GetDatabase().StringGetAsync(key);
                if (value.HasValue)
                {
                    //需要用的反序列化,将Redis存储的Byte[],进行反序列化
                    return SerializeHelper.Deserialize<TEntity>(value);
                }
                else
                {
                    return default;
                }
            }
    
            public async Task RemoveAsync(string key)
            {
                await redisConnection.GetDatabase().KeyDeleteAsync(key);
            }
    
            public async Task RemoveByKey(string key)
            {
                var redisResult = await redisConnection.GetDatabase().ScriptEvaluateAsync(LuaScript.Prepare(
                    //Redis的keys模糊查询:
                    " local res = redis.call('KEYS', @keypattern) " +
                    " return res "), new { @keypattern = key });
    
                if (!redisResult.IsNull)
                {
                    var keys = (string[])redisResult;
                    foreach (var k in keys)
                        redisConnection.GetDatabase().KeyDelete(k);
    
                }
            }
    
            public async Task SetAsync(string key, object value, TimeSpan cacheTime)
            {
                if (value != null)
                {
                    //序列化,将object值生成RedisValue
                    await redisConnection.GetDatabase().StringSetAsync(key, SerializeHelper.Serialize(value), cacheTime);
                }
            }
    
            public async Task<bool> SetValueAsync(string key, byte[] value)
            {
                return await redisConnection.GetDatabase().StringSetAsync(key, value, TimeSpan.FromSeconds(120));
            }

    定义缓存特性

    Common层新增Attributes文件夹,并新建CachingAttribute来定义我们的redis缓存特性,有了这个特性,我们就可以控制方法是否需要缓存到redis。

        /// <summary>
        /// 这个Attribute就是使用时候的验证,把它添加到要缓存数据的方法中,即可完成缓存的操作。
        /// </summary>
        [AttributeUsage(AttributeTargets.Method, Inherited = true)]
        public class CachingAttribute : Attribute
        {
            //过期时间
            public int AbsoluteExpiration { get; set; }
           /// <summary>
           /// 自定义key
           /// </summary>
            public string CustomKeyValue { get; set; }
            /// <summary>
            /// 是否删除
            /// </summary>
            public bool IsDelete { get; set; } = false;
        }

    定义Redis切面拦截器

    在主工程新加AOP文件夹,新建CacheAOPbase和RedisCacheAOP文件

    public abstract class CacheAOPbase : IInterceptor
        {
            /// <summary>
            /// AOP的拦截方法
            /// </summary>
            /// <param name="invocation"></param>
            public abstract void Intercept(IInvocation invocation);
    
            /// <summary>
            /// 自定义缓存的key
            /// </summary>
            /// <param name="invocation"></param>
            /// <returns></returns>
            protected string CustomCacheKey(IInvocation invocation)
            {
                var typeName = invocation.TargetType.Name;
                var methodName = invocation.Method.Name;
                var methodArguments = invocation.Arguments.Select(GetArgumentValue).Take(3).ToList();//获取参数列表,最多三个
    
                string key = $"{typeName}:{methodName}:";
                foreach (var param in methodArguments)
                {
                    key = $"{key}{param}:";
                }
    
                return key.TrimEnd(':');
            }
    
            /// <summary>
            /// object 转 string
            /// </summary>
            /// <param name="arg"></param>
            /// <returns></returns>
            protected static string GetArgumentValue(object arg)
            {
                if (arg is DateTime || arg is DateTime?)
                    return ((DateTime)arg).ToString("yyyyMMddHHmmss");
    
                if (arg is string || arg is ValueType || arg is Nullable)
                    return arg.ToString();
    
                if (arg != null)
                {
                    if (arg is Expression)
                    {
                        var obj = arg as Expression;
                        var result = Resolve(obj);
                        return Common.Helper.MD5Helper.MD5Encrypt16(result);
                    }
                    else if (arg.GetType().IsClass)
                    {
                        return Common.Helper.MD5Helper.MD5Encrypt16(Newtonsoft.Json.JsonConvert.SerializeObject(arg));
                    }
                }
                return string.Empty;
            }
    
            private static string Resolve(Expression expression)
            {
                if (expression is LambdaExpression)
                {
                    LambdaExpression lambda = expression as LambdaExpression;
                    expression = lambda.Body;
                    return Resolve(expression);
                }
                if (expression is BinaryExpression)
                {
                    BinaryExpression binary = expression as BinaryExpression;
                    if (binary.Left is MemberExpression && binary.Right is ConstantExpression)//解析x=>x.Name=="123" x.Age==123这类
                        return ResolveFunc(binary.Left, binary.Right, binary.NodeType);
                    if (binary.Left is MethodCallExpression && binary.Right is ConstantExpression)//解析x=>x.Name.Contains("xxx")==false这类的
                    {
                        object value = (binary.Right as ConstantExpression).Value;
                        return ResolveLinqToObject(binary.Left, value, binary.NodeType);
                    }
                    if ((binary.Left is MemberExpression && binary.Right is MemberExpression)
                        || (binary.Left is MemberExpression && binary.Right is UnaryExpression))//解析x=>x.Date==DateTime.Now这种
                    {
                        LambdaExpression lambda = Expression.Lambda(binary.Right);
                        Delegate fn = lambda.Compile();
                        ConstantExpression value = Expression.Constant(fn.DynamicInvoke(null), binary.Right.Type);
                        return ResolveFunc(binary.Left, value, binary.NodeType);
                    }
                }
                if (expression is UnaryExpression)
                {
                    UnaryExpression unary = expression as UnaryExpression;
                    if (unary.Operand is MethodCallExpression)//解析!x=>x.Name.Contains("xxx")或!array.Contains(x.Name)这类
                        return ResolveLinqToObject(unary.Operand, false);
                    if (unary.Operand is MemberExpression && unary.NodeType == ExpressionType.Not)//解析x=>!x.isDeletion这样的 
                    {
                        ConstantExpression constant = Expression.Constant(false);
                        return ResolveFunc(unary.Operand, constant, ExpressionType.Equal);
                    }
                }
                if (expression is MemberExpression && expression.NodeType == ExpressionType.MemberAccess)//解析x=>x.isDeletion这样的 
                {
                    MemberExpression member = expression as MemberExpression;
                    ConstantExpression constant = Expression.Constant(true);
                    return ResolveFunc(member, constant, ExpressionType.Equal);
                }
                if (expression is MethodCallExpression)//x=>x.Name.Contains("xxx")或array.Contains(x.Name)这类
                {
                    MethodCallExpression methodcall = expression as MethodCallExpression;
                    return ResolveLinqToObject(methodcall, true);
                }
                //已经修改过代码body应该不会是null值了
                if (!(expression is BinaryExpression body))
                    return string.Empty;
                var Operator = GetOperator(body.NodeType);
                var Left = Resolve(body.Left);
                var Right = Resolve(body.Right);
                string Result = string.Format("({0} {1} {2})", Left, Operator, Right);
                return Result;
            }
    
            private static string GetOperator(ExpressionType expressiontype)
            {
                return expressiontype switch
                {
                    ExpressionType.And => "and",
                    ExpressionType.AndAlso => "and",
                    ExpressionType.Or => "or",
                    ExpressionType.OrElse => "or",
                    ExpressionType.Equal => "=",
                    ExpressionType.NotEqual => "<>",
                    ExpressionType.LessThan => "<",
                    ExpressionType.LessThanOrEqual => "<=",
                    ExpressionType.GreaterThan => ">",
                    ExpressionType.GreaterThanOrEqual => ">=",
                    _ => throw new Exception(string.Format("不支持{0}此种运算符查找!" + expressiontype)),
                };
            }
    
            private static string ResolveFunc(Expression left, Expression right, ExpressionType expressiontype)
            {
                var Name = (left as MemberExpression).Member.Name;
                var Value = (right as ConstantExpression).Value;
                var Operator = GetOperator(expressiontype);
                return Name + Operator + Value ?? "null";
            }
    
            private static string ResolveLinqToObject(Expression expression, object value, ExpressionType? expressiontype = null)
            {
                var MethodCall = expression as MethodCallExpression;
                var MethodName = MethodCall.Method.Name;
                switch (MethodName)
                {
                    case "Contains":
                        if (MethodCall.Object != null)
                            return Like(MethodCall);
                        return In(MethodCall, value);
                    case "Count":
                        return Len(MethodCall, value, expressiontype.Value);
                    case "LongCount":
                        return Len(MethodCall, value, expressiontype.Value);
                    default:
                        throw new Exception(string.Format("不支持{0}方法的查找!", MethodName));
                }
            }
    
            private static string In(MethodCallExpression expression, object isTrue)
            {
                var Argument1 = (expression.Arguments[0] as MemberExpression).Expression as ConstantExpression;
                var Argument2 = expression.Arguments[1] as MemberExpression;
                var Field_Array = Argument1.Value.GetType().GetFields().First();
                object[] Array = Field_Array.GetValue(Argument1.Value) as object[];
                List<string> SetInPara = new List<string>();
                for (int i = 0; i < Array.Length; i++)
                {
    
                    string Value = Array[i].ToString();
                    SetInPara.Add(Value);
                }
                string Name = Argument2.Member.Name;
                string Operator = Convert.ToBoolean(isTrue) ? "in" : " not in";
                string CompName = string.Join(",", SetInPara);
                string Result = string.Format("{0} {1} ({2})", Name, Operator, CompName);
                return Result;
            }
            private static string Like(MethodCallExpression expression)
            {
    
                var Temp = expression.Arguments[0];
                LambdaExpression lambda = Expression.Lambda(Temp);
                Delegate fn = lambda.Compile();
                var tempValue = Expression.Constant(fn.DynamicInvoke(null), Temp.Type);
                string Value = string.Format("%{0}%", tempValue);
                string Name = (expression.Object as MemberExpression).Member.Name;
                string Result = string.Format("{0} like {1}", Name, Value);
                return Result;
            }
    
    
            private static string Len(MethodCallExpression expression, object value, ExpressionType expressiontype)
            {
                object Name = (expression.Arguments[0] as MemberExpression).Member.Name;
                string Operator = GetOperator(expressiontype);
                string Result = string.Format("len({0}){1}{2}", Name, Operator, value.ToString());
                return Result;
            }
    
        }
    CacheAOPbase
     /// <summary>
        /// 面向切面的缓存使用
        /// </summary>
        public class RedisCacheAOP : CacheAOPbase
        {
            //通过注入的方式,把缓存操作接口通过构造函数注入
            private readonly IRedisCacheManager _cache;
            public RedisCacheAOP(IRedisCacheManager cache)
            {
                _cache = cache;
            }
    
            //Intercept方法是拦截的关键所在,也是IInterceptor接口中的唯一定义
            //代码已经合并 ,学习pr流程
            public override void Intercept(IInvocation invocation)
            {
                var method = invocation.MethodInvocationTarget ?? invocation.Method;
                if (method.ReturnType == typeof(void) || method.ReturnType == typeof(Task))
                {
                    invocation.Proceed();
                    return;
                }
                //对当前方法的特性验证
    
                if (method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute)) is CachingAttribute qCachingAttribute)
                {
                    //获取自定义缓存键
                    var cacheKey = qCachingAttribute.CustomKeyValue ?? CustomCacheKey(invocation);
                    //注意是 string 类型,方法GetValue
                    var cacheValue = _cache.GetValue(cacheKey);
                    if (cacheValue != null)
                    {
                        if (qCachingAttribute.IsDelete)
                        {
                            //删除Redis里面的数据
                            _cache.Remove(cacheKey);
                        }
                        else
                        {
                            //将当前获取到的缓存值,赋值给当前执行方法
                            Type returnType;
                            if (typeof(Task).IsAssignableFrom(method.ReturnType))
                            {
                                returnType = method.ReturnType.GenericTypeArguments.FirstOrDefault();
                            }
                            else
                            {
                                returnType = method.ReturnType;
                            }
                            dynamic _result = Newtonsoft.Json.JsonConvert.DeserializeObject(cacheValue, returnType);
                            invocation.ReturnValue = (typeof(Task).IsAssignableFrom(method.ReturnType)) ? Task.FromResult(_result) : _result;
                            return;
                        }
                    }
                    //去执行当前的方法
                    invocation.Proceed();
    
                    //存入缓存
                    if (!string.IsNullOrWhiteSpace(cacheKey) && qCachingAttribute.IsDelete == false)
                    {
                        object response;
    
                        //Type type = invocation.ReturnValue?.GetType();
                        var type = invocation.Method.ReturnType;
                        if (typeof(Task).IsAssignableFrom(type))
                        {
                            var resultProperty = type.GetProperty("Result");
                            response = resultProperty.GetValue(invocation.ReturnValue);
                        }
                        else
                        {
                            response = invocation.ReturnValue;
                        }
                        if (response == null) response = string.Empty;
    
                        _cache.Set(cacheKey, response, TimeSpan.FromMinutes(qCachingAttribute.AbsoluteExpiration));
                    }
                }
                else
                {
                    invocation.Proceed();//直接执行被拦截方法
                }
            }
        }
    RedisCacheAOP

    注入到服务到 Autofac

    Setup的AutofacModuleRegister文件注入redisaop缓存

           protected override void Load(ContainerBuilder builder)
            {
                //注册RedisAop
                builder.RegisterType<RedisCacheAOP>();
                //注册Service
                var assemblysServices = Assembly.Load("Webapi.Core.Service");
                builder.RegisterAssemblyTypes(assemblysServices)
                    .InstancePerDependency()//瞬时单例
                   .AsImplementedInterfaces()////自动以其实现的所有接口类型暴露(包括IDisposable接口)
                   .EnableInterfaceInterceptors() //引用Autofac.Extras.DynamicProxy;
                   .InterceptedBy(typeof(RedisCacheAOP));//可以放一个AOP拦截器集合
    
                //注册Repository
                var assemblysRepository = Assembly.Load("Webapi.Core.Repository");
                builder.RegisterAssemblyTypes(assemblysRepository)
                    .InstancePerDependency()//瞬时单例
                   .AsImplementedInterfaces()////自动以其实现的所有接口类型暴露(包括IDisposable接口)
                   .EnableInterfaceInterceptors(); //引用Autofac.Extras.DynamicProxy;
    
            }

    测试APO缓存

    User控制器新建AopTest接口

             /// <summary>
            /// 测试aop缓存redis
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            public async Task<IActionResult> AopTest(int id)
            {
                var sucess = await _userService.GetUserDetails(id);
                return Ok(sucess);
            }
    userService的GetUserDetails方法上方标注特性
            [Caching(AbsoluteExpiration =1)]
            public async Task<UserViewModel> GetUserDetails(int id)
            {
                var userinfo = await userDal.GetById(id);
                if (userinfo != null)
                {
                    //UserViewModel model = new UserViewModel()
                    //{
                    //    UserId = userinfo.UserId,
                    //    UserName = userinfo.UserName,
                    //    Address = "北京市xx区xx小区",
                    //    Age = userinfo.Age,
                    //    Birthday = "1996-06-26",
                    //    Phone = "13888888888"
    
                    //};
                    UserViewModel model = iMapper.Map<UserViewModel>(userinfo);
                    model.Address = "北京市xx区xx小区";
                    model.Birthday = "1996-06-26";
                    model.Phone = "13888888888";
                    return model;
    
                }
                else
                {
                    return null;
                }
            }

    断点达到的时候可以看到没有数据

     再次执行Aoptest接口,可以看到数据已经被缓存了。这样我们就实现了基于AOP的缓存

  • 相关阅读:
    [css]浮动造成的影响
    [py]django的manytomany字段和后台搜索过滤功能
    [py][lc]python高阶函数(匿名/map/reduce/sorted)
    [py][lc]python的纸牌知识点
    [js]js中类的继承
    [js]js杂项陆续补充中...
    [js]js设计模式小结
    WP10的一点小问题
    JS 判断滚动底部并加载更多效果。。。。。。。。。
    This assembly may have been downloaded from the Web. ......
  • 原文地址:https://www.cnblogs.com/huguodong/p/13414075.html
Copyright © 2011-2022 走看看