zoukankan      html  css  js  c++  java
  • .NET Core 3.x 基于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分钟

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

  • 相关阅读:
    Postman高级应用——流程控制、调试、公共函数、外部数据文件
    python模块之openpyxl
    appium实例1:启动淘宝app
    appium1.6在mac上环境搭建启动ios模拟器上Safari浏览器 转自:上海-悠悠
    appium 环境准备
    python_正则表达式
    接口测试指导方案 转:紫漪
    性能监控工具的配置及使用
    Python_斐波那契数列
    Js new一个函数和直接调用函数的区别
  • 原文地址:https://www.cnblogs.com/zhaoshujie/p/13666051.html
Copyright © 2011-2022 走看看