一、依赖包

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

    /// <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分钟



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

.NET Core 3.x 基于Autofac的AOP缓存的更多相关文章

  1. Asp.net Core 3.1基于AspectCore实现AOP,实现事务、缓存拦截器

    最近想给我的框架加一种功能,就是比如给一个方法加一个事务的特性Attribute,那这个方法就会启用事务处理.给一个方法加一个缓存特性,那这个方法就会进行缓存. 这个也是网上说的面向切面编程AOP. ...

  2. .NET Core下自带容器IServiceCollection以及AutoFac以及AutoFac中AOP简介

    https://www.cnblogs.com/artech/p/net-core-di-01.html 大内老A的在.NET Core下对这些的介绍,有一系列文章 https://www.cnblo ...

  3. Autofac实现AOP拦截

    本文主要是详解一下在ASP.NET Core中,采用替换后的Autofac来实现AOP拦截. Aspect Oriented Programming(AOP),面向切面编程,是一个比较热门的话题.AO ...

  4. ASP.Net Core 3.1 With Autofac ConfigureServices returning an System.IServiceProvider isn't supported.

    ASP.Net Core 3.1 With Autofac ConfigureServices returning an System.IServiceProvider isn't supported ...

  5. 基于autofac的属性注入

    基于autofac的属性注入 什么是属性注入 在了解属性注入之前,要先了解一下DI(Dependency Injection),即依赖注入.在ASP.NET Core里自带了一个IOC容器,而且程序支 ...

  6. 基于Schema的AOP 配置使用详解

    原文地址:http://jinnianshilongnian.iteye.com/blog/1418598 基于Schema的AOP从Spring2.0之后通过"aop"命名空间来 ...

  7. 开涛spring3(6.3) - AOP 之 6.3 基于Schema的AOP

    6.3  基于Schema的AOP 基于Schema的AOP从Spring2.0之后通过“aop”命名空间来定义切面.切入点及声明通知. 在Spring配置文件中,所以AOP相关定义必须放在<a ...

  8. spring aop 基于schema的aop

    AOP的基本概念: 连接点(Jointpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化.方法执行.方法调用.字段调用或处理异常等等,Spring只支持方法执行连接点,在AOP ...

  9. Spring_Spring与AOP_AspectJ基于注解的AOP实现

    一.AspectJ.Spring与AOP的关系 AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法,所以它有一个专门的编译器用来生成遵守Java字节编码规范的Cl ...

  10. .Net Core 学习之路-AutoFac的使用

    本文不介绍IoC和DI的概念,如果你对Ioc之前没有了解的话,建议先去搜索一下相关的资料 这篇文章将简单介绍一下AutoFac的基本使用以及在asp .net core中的应用 Autofac介绍 组 ...

随机推荐

  1. TDSQL数据库考试实操题

    第一题: 演练二 物理备份(5分) 答: 第二题:2. 演练一 请根据给定的演练方案,进行相关演练,并按如下要求提交截图 主备切换(5分) 答: 第三题:3. 演练一 请根据给定的演练方案,进行相关演 ...

  2. redis RDB AOF数据持久化

    目录 redis RDB持久化[手工持久化]: redis RDB持久化条件配置[适合用于备份]redis rdb持久化策略 redis AOF持久化 redis AOF持久化配置 redis RDB ...

  3. ubuntu下openCV-Haar特征分类器训练

    ubuntu下openCV-Haar特征分类器训练 这段时间在学openCV,准备做一个头部检测,但是openCV自带的分类器只有人脸检测的,而且准确度不高,就准备自己训练一个分类器.在网上看了很多的 ...

  4. 面试必会 --> MyBatis篇

    什么是MyBatis Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动.创建连接.创建statement等繁杂的过程 ...

  5. 铭瑄B760 ITX 无法睿频 无法跑满

    铭瑄B760 ITX 无法睿频 无法跑满 状况: 铭瑄B760 ITX + 12600K,跑分时,大核最高 3.7GHz,电压也不到1V.CPU-Z 跑分才600. 解决方法: 1.关机. 2.长按 ...

  6. asp.net中gridview隐藏一列并且使用这列数据的方法

    方法一:在RowCreated事件中添加如下代码可隐藏此列,使用this.grdView.Rows[index].Cells[1].Text获取值,index为行号. 1 protected void ...

  7. 简单易懂的JSON框架

      分享一个由本人编写的JSON框架.   JSON反序列化使用递归方式来解析JSON字符串,不使用任何第三方JAR包,只使用JAVA的反射来创建对象(必须要有无参构造器),赋值,编写反射缓存来提升性 ...

  8. Ton 区块链的官方 类ERC20-Token 智能合约代码-Transfer部分解析

    作者:林冠宏 / 指尖下的幽灵.转载者,请: 务必标明出处. 掘金:https://juejin.im/user/1785262612681997 GitHub : https://github.co ...

  9. spring项目中读取resources下的文件

    spring项目中读取resources下的文件 我们都知道,spring项目经过maven打包后,可以打成jar包或可解压的war包 a. war包是需要外置的web容器去运行的,是需要先解压的 b ...

  10. 哎,被这个叫做at least once的玩意坑麻了。

    你好呀,我是歪歪. 前几天遇到一个生产问题,同一个数据在数据库里面被插入了两次,导致后续处理出现了一些问题. 当时我们首先检讨了自己,没有做好幂等校验.甚至还发现了一个低级错误:对应的表,针对订单号, ...