zoukankan      html  css  js  c++  java
  • NET Core 3.1 基于Autofac 的缓存AOP 实现自定义缓存

    上一节是全部缓存,很浪费内存,所有很多时候我们只是缓存几个方法,下面是自定义缓存代码:

    一、依赖包

    二、定义一个简单的缓存接口

    /// <summary>
        /// 简单的缓存接口,只有查询和添加,以后会进行扩展
        /// </summary>
        public interface ICaching
        {
            object Get(string cacheKey);
            void Set(string cacheKey, object cacheValue, TimeSpan absoluteExpirationRelativeToNow);
        }

    三、实现缓存接口

    public class MemoryCaching : ICaching
        {
            private IMemoryCache _cache;
            public MemoryCaching(IMemoryCache cache)
            {
                _cache = cache;
            }
            public object Get(string cacheKey)
            {
                return _cache.Get(cacheKey);
            }
            public void Set(string cacheKey, object cacheValue, TimeSpan absoluteExpirationRelativeToNow)
            {
                _cache.Set(cacheKey, cacheValue, absoluteExpirationRelativeToNow);
            }
        }

    四、定义缓存属性

    /// <summary>
        /// 这个Attribute就是使用时候的验证,把它添加到要缓存数据的方法中,即可完成缓存的操作。
        /// 往Server层方法上添加即可使用 [CachingAttribute(AbsoluteExpiration = 10)] //使用缓存AOP 缓存10分钟
        /// </summary>
        [AttributeUsage(AttributeTargets.Method, Inherited = true)]
        public class CachingAttribute : Attribute
        {
            /// <summary>
            /// 缓存绝对过期时间(分钟)
            /// </summary>
            public int AbsoluteExpiration { get; set; } = 30;
    
        }

    五、AOP实现

    /// <summary>
        /// 面向切面的缓存使用
        /// </summary>
        public class BlogCacheAOP : IInterceptor
        {
            //通过注入的方式,把缓存操作接口通过构造函数注入
            private readonly ICaching _cache;
            public BlogCacheAOP(ICaching cache)
            {
                _cache = cache;
            }
    
            //Intercept方法是拦截的关键所在,也是IInterceptor接口中的唯一定义
            public void Intercept(IInvocation invocation)
            {
                var method = invocation.MethodInvocationTarget ?? invocation.Method;
                //对当前方法的特性验证
                var qCachingAttribute = this.GetQCachingAttributeInfo(invocation.MethodInvocationTarget ?? invocation.Method);
                if (qCachingAttribute != null)
                {
                    ProceedCaching(invocation, qCachingAttribute);
                }
                else
                {
                    invocation.Proceed();//直接执行被拦截方法
                }
            }
    
            private CachingAttribute GetQCachingAttributeInfo(MethodInfo method)
            {
                return method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute)) as CachingAttribute;
            }
    
            private void ProceedCaching(IInvocation invocation, CachingAttribute attribute)
            {
                //获取自定义缓存键
                var cacheKey = CustomCacheKey(invocation);
                //根据key获取相应的缓存值
                var cacheValue = _cache.Get(cacheKey);
                if (cacheValue != null)
                {
                    //将当前获取到的缓存值,赋值给当前执行方法
                    invocation.ReturnValue = cacheValue;
                    return;
                }
                //去执行当前的方法
                invocation.Proceed();
                //存入缓存
                if (!string.IsNullOrWhiteSpace(cacheKey))
                {
                    _cache.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromMinutes(attribute.AbsoluteExpiration));
                }
            }
    
            /// <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);
                }
                var body = expression as BinaryExpression;
                //已经修改过代码body应该不会是null值了
                if (body == null)
                    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)
            {
                switch (expressiontype)
                {
                    case ExpressionType.And:
                        return "and";
                    case ExpressionType.AndAlso:
                        return "and";
                    case ExpressionType.Or:
                        return "or";
                    case ExpressionType.OrElse:
                        return "or";
                    case ExpressionType.Equal:
                        return "=";
                    case ExpressionType.NotEqual:
                        return "<>";
                    case ExpressionType.LessThan:
                        return "<";
                    case ExpressionType.LessThanOrEqual:
                        return "<=";
                    case ExpressionType.GreaterThan:
                        return ">";
                    case ExpressionType.GreaterThanOrEqual:
                        return ">=";
                    default:
                        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 Name_para = "InParameter" + 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;
            }
        }

    六、注入缓存

    /// <summary>
        /// 缓存服务启动
        /// </summary>
        public static class MemoryCacheSetup
        {
            public static void AddMemoryCacheSetup(this IServiceCollection services)
            {
                if (services == null) throw new ArgumentNullException(nameof(services));
    
                services.AddScoped<ICaching, MemoryCaching>();
                services.AddSingleton<IMemoryCache>(factory =>
                {
                    var cache = new MemoryCache(new MemoryCacheOptions());
                    return cache;
                });
            }
        }
    public class Startup
        {        // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {//注入缓存
                services.AddMemoryCacheSetup();
            }
        }

    下面两步骤需要根据自己服务层修改:

    七、注册AOP

    public class Startup
        {
            public void ConfigureContainer(ContainerBuilder builder)
            {
                //other ...// AOP 
                var cacheType = new List<Type>();
                
                builder.RegisterType<BlogCacheAOP>();
                cacheType.Add(typeof(BlogCacheAOP));
                // 获取 Service.dll 程序集服务,并注册
                var assemblysServices = Assembly.LoadFrom(servicesDllFile);
                builder.RegisterAssemblyTypes(assemblysServices)
                            .AsImplementedInterfaces()
                            .InstancePerDependency()
                            .EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
                            .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。
    
                //other ...
    
    
            }
    
           
        }

    八、使用

    服务层在需要缓存的方法上添加属性,就OK了

    public class BlogArticleServices : BaseServices<BlogArticle>, IBlogArticleServices
        {
            IBlogArticleRepository _dal;
    
            public BlogArticleServices(IBlogArticleRepository dal)
            {
                this._dal = dal;
                base.BaseDal = dal;
            }
    
            /// <summary>
            /// 获取博客列表
            /// </summary>
            /// <returns></returns>
            [CachingAttribute(AbsoluteExpiration = 10)] //使用缓存AOP 缓存10分钟
            public async Task<List<BlogArticle>> getBlogs()
            {
                var blogList = await _dal.Query(a => a.bID > 0, a => a.bID);
                return blogList;
            }
        }

    九、运行代码

    第一次进入没有缓存,走 set 缓存10分钟

     第二次进入,有缓存得到缓存直接返回

    大功告成,

  • 相关阅读:
    windows域相关
    IDEA 找不到maven编译命令操作
    Idea Cannot import to svn: Cannot run program "svn"
    NodeJS在CentOs7下安装
    NodeJS 安装不存在的模块
    NodeJS淘宝 CNPM 镜像
    Intellij Idea 使用入门教程
    Java中基本类型占用字节数
    JWT—JSON Web Token
    2016年度最受欢迎中国开源软件
  • 原文地址:https://www.cnblogs.com/shuaichao/p/12404890.html
Copyright © 2011-2022 走看看