本文主要介绍Asp.net Core 3.1基于AspectCore的事务和缓存拦截器函数的AOP实现。这篇文章很详细,对你的学习或者工作有一定的参考价值。有需要的朋友可以参考一下。
最近想给自己的框架增加一个功能,就是比如给一个方法增加一个事务属性,那么这个方法就会启用事务处理。给一个方法添加一个缓存特性,这个方法就会缓存。
这也是面向方面编程的在线AOP。
AOP的概念也很好理解。它类似于中间件。说白了,我可以随意在方法前面或者后面添加代码,非常适合缓存、日志等处理。
我在net core2.2的时候,当时尝试用autofac实现aop,但是这次不想用autofac了。我用了一个更轻便的框架,AspectCore。
用起来非常非常简单,只是一开始走了一点弯路。主要是网core3以下的教程都在网上,3以下的用法和以前有些不同。
先安装NuGet包,包名:AspectCore。扩展。依赖注入
然后在Program.cs类中添加一行代码,这就是网芯3的区别。这个添加的代码意味着用AspectCore替换内置的IOC容器。因为AOP依赖IOC,所以内置的IOC必须更换。
公开课程
{
公共静态void Main(string[] args)
{
CreateHostBuilder(参数)。构建()。run();
}
公共静态IHostBuilder CreateHostBuilder(string[]args)=1
主持人。CreateDefaultBuilder(参数)。ConfigureWebHostDefaults(web builder=
{
webBuilder。UseStartupStartup();
})
//用AspectCore替换默认的IOC容器。UseServiceProviderFactory(new DynamicProxyServiceProviderFactory());
}
然后在Startup.cs类的ConfigureServices中添加代码。(其实这个可以加,也可以不加。如果需要配置,可以添加,比如全局拦截器,只拦截哪些匹配的服务。因为我只用特性拦截,所以什么都没配置。)
服务。ConfigureDynamicProxy(o={
//添加AOP的配置
});
那么AOP配置好了,不是很简单吗?
当然,你也要注意使用。您可以截取接口、接口的方法、类和类的虚方法。而如果要拦截控制器的动作,则需要在ConfigureService中添加AddControllerAsServices。
服务。AddControllers()
//将控制器视为服务。AddControllersAsServices()
下面列出了我的事务拦截器代码。如果是特征拦截,我会继承AbstractInterceptorAttribute。如果我想写一个全局拦截器,我会抽象拦截器,然后在ConfigureDynamicProxy中配置它。这个我就不介绍了。
如果您的拦截器放在其他项目中,记得添加AspectCore。核心包,不仅仅是AspectCore。摘要。我只加了AspectCore。开头抽象,但是我一直没找到IsAsync,UnwrapAsyncReturnValue之类的一些扩展方法。
公共类TransactionInterceptorAttribute:AbstractInterceptorAttribute
{
公共异步重写任务调用(AspectContext上下文,下一个AspectDelegate)
{
var dbContext=context。service provider . GetServiceAppDbContext();
//先判断事务是否已经启用。
if (dbContext。database . current transaction==null)
{
等待dbContext。database . BeginTransactionAsync();
尝试
{
等待下一个(上下文);
dbContext。database . commit transaction();
}
catch(例外ex)
{
dbContext。database . roll back transaction();
扔ex;
}
}
其他
{
等待下一个(上下文);
}
}
}
然后我就可以如此优雅地使用事务了。
我再列出我的缓存拦截器,(感谢网友的提醒,我做了一下修改,针对异步方法返回值的处理),对了,下面的ICacheHelper是我定义的一个缓存助手接口,用的是redis,我会在后面写一篇博客
公共类CacheInterceptorAttribute:AbstractInterceptorAttribute
{
///摘要
///缓存秒数
////摘要
public int ExpireSeconds { get设置;}
公共异步重写任务调用(aspect上下文上下文,下一个AspectDelegate)
{
//判断是否是异步方法
bool isAsync=上下文isa sync();
//如果(上下文实现方法。GetCustomAttribute(类型为(AsyncStateMachineAttribute))!=空)
//{
//is async=true;
//}
//先判断方法是否有返回值,无就不进行缓存判断
var methodReturnType=上下文GetReturnParameter().类型;
if(method returntype==类型(void)| | method returntype==类型(任务)| | method returntype==类型(值任务))
{
等待下一个(上下文);
返回;
}
var returnType=方法returnType;
if (isAsync)
{
//取得异步返回的类型
returnType=returnType .GenericTypeArguments。FirstOrDefault();
}
//获取方法参数名
string param=CommonHelper .ObjectToJsonString(上下文。参数);
//获取方法名称,也就是缓存键值
string key='Methods:' context .实现方法。声明类型。全名。'语境ImplementationMethod。名字
var缓存=上下文服务提供商。GetServiceICacheHelper();
//如果缓存有值,那就直接返回缓存值
如果(缓存HashExists(key,param))
{
//反射获取缓存值,相当于缓存HashGet(key,param)
var value=typeof(ICacheHelper).GetMethod(nameof(ICacheHelper .HashGet)).MakeGenericMethod(returnType).Invoke(cache,new[] { key,param });
if (isAsync)
{
//判断是工作还是价值任务
if(method returntype==任务类型).MakeGenericType(returnType))
{
//反射获取工作类型的返回值,相当于任务。来自结果(值)
语境ReturnValue=typeof(任务)。获取方法(任务的名称FromResult)).MakeGenericMethod(returnType).Invoke(null,new[] { value }).
}
else if(method returntype==type of(value task).MakeGenericType(returnType))
{
//反射构建价值任务类型的返回值,相当于新值任务(值)
语境ReturnValue=激活器创建实例(值任务的类型).MakeGenericType(returnType),value);
}
}
其他
{
语境ReturnValue=值;
}
返回;
}
等待下一个(上下文);
对象返回值;
if (isAsync)
{
返回值=等待上下文unwapasyncreturnvalue();
//反射获取异步结果的值,相当于(上下文。返回值作为任务)。结果
//returnValue=typeof(Task).MakeGenericType(returnType).GetProperty(nameof(Taskobject .结果))。GetValue(上下文return value);
}
其他
{
返回值=上下文ReturnValue
}
缓存HashSet(key,param,返回值);
if (ExpireSeconds 0)
{
缓存SetExpire(key,TimeSpan .从秒(过期秒));
}
}
}
我还弄了一个缓存删除拦截器,作用就是带有这个特性的方法执行后,会删除相关缓存值
为什么有这个设计呢,比如说我给一个方法GetUserList加了缓存,那我数据改变了怎么办,我想在用户数据改变时,把这个缓存删除掉,那我就可以在保存用户方法上加上我这个缓存删除拦截器,那这个方法执行后,就会把相关的缓存删除掉了
公共类CacheDeleteInterceptorAttribute:AbstractInterceptorAttribute
{
私有只读类型[]_类型;
私有只读字符串[]_方法;
///摘要
///需要传入相同数量的类型和方法,相同位置的类型和方法会组合成一个缓存键进行删除。
////摘要
///param name='Types '在类中传递以删除缓存/param
///param name='Methods '传入的用于删除缓存的方法的名称必须对应于类型array/param
公共CacheDeleteInterceptorAttribute(Type[]类型,string[]方法)
{
如果(类型。长度!=方法。长度)
{
抛出新的apifailexception (apifailcode。operation _ fail,'类型必须与方法数相同');
}
_types=类型;
_methods=方法;
}
公共异步重写任务调用(AspectContext上下文,下一个AspectDelegate)
{
var cache=上下文。service provider . GetServiceICacheHelper();
等待下一个(上下文);
for(int I=0;I _类型。长度;我)
{
var type=_ types[I];
var method=_ methods[I];
string key='Methods:' type。全名“.”方法;
缓存。删除(键);
}
}
}
我还想象了AOP的实现原理:
要实现AOP,我们需要依靠IOC容器,因为它是我们类的管家。可以拦截的类必须是IOC注入的,自己新出来的不拦截。如果我想在方法A前面添加一些代码,我会告诉IOC把代码给它。IOC注入方法A的类时,会继承它生成一个派生类,然后重写方法A,所以拦截方法必须是虚的,那么我要添加的代码就写在方法A中,然后是base。一个()是这样的。
关于Asp.net核心3.1基于AspectCore实现AOP事务和缓存拦截器功能的文章到此结束。有关Asp.net核心3.1实现事务和缓存拦截器的更多信息,请搜索我们以前的文章或继续浏览以下相关文章。希望大家以后能多多支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。