最近想给我的框架加一种功能,就是比如给一个方法加一个事务的特性Attribute,那这个方法就会启用事务处理。给一个方法加一个缓存特性,那这个方法就会进行缓存。
这个也是网上说的面向切面编程AOP。
AOP的概念也很好理解,跟中间件差不多,说白了,就是我可以任意地在方法的前面或后面添加代码,这很适合用于缓存、日志等处理。
在net core2.2时,我当时就尝试过用autofac实现aop,但这次我不想用autofac,我用了一个更轻量级的框架,AspectCore。
用起来非常非常的简单,但一开始还是走了一点弯路,主要是网上都是net core3以下的教程,3以下的使用方法跟之前有一些不同。
先安装NuGet包,包名:AspectCore.Extensions.DependencyInjection
然后在Program.cs类中增加一行代码,这是net core 3的不同之处,这句添加的代码,意思就是用AspectCore的IOC容器替换内置的。因为AOP需要依靠IOC实现,所以必须得替换掉内置的IOC。
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) //用AspectCore替换默认的IOC容器 .UseServiceProviderFactory(new DynamicProxyServiceProviderFactory()); }
然后在Startup.cs类中的ConfigureServices中添加代码。(其实这个加不加都可以,如果需要配置就加,例如全局的拦截器、只拦截哪些匹配的服务,因为我只用特性进行拦截,所以我就什么也没配置)
services.ConfigureDynamicProxy(o=> { //添加AOP的配置 });
这样AOP就配置好了,是不是很简单。
当然使用方面也需要注意一下,可以在接口、接口的方法、类,类的virtual方法上进行拦截。还有如果你想拦截控制器的action的话,那需要在ConfigureService里AddControllerAsServices
services.AddControllers() //把控制器当成服务 .AddControllersAsServices()
下面我列出我的事务拦截器代码,如果是特性拦截,就继承AbstractInterceptorAttribute,如果要写一个全局拦截器,就AbstractInterceptor,然后在ConfigureDynamicProxy中进行配置,这个我就不介绍了
如果你的拦截器是放在其他项目的,那要记得添加AspectCore.Core包,不要只添加AspectCore.Abstractions,我一开始就只添加了AspectCore.Abstractions,一直没发现IsAsync、UnwrapAsyncReturnValue等一些扩展方法。
public class TransactionInterceptorAttribute : AbstractInterceptorAttribute { public async override Task Invoke(AspectContext context, AspectDelegate next) { var dbContext = context.ServiceProvider.GetService<AppDbContext>(); //先判断是否已经启用了事务 if (dbContext.Database.CurrentTransaction == null) { await dbContext.Database.BeginTransactionAsync(); try { await next(context); dbContext.Database.CommitTransaction(); } catch (Exception ex) { dbContext.Database.RollbackTransaction(); throw ex; } } else { await next(context); } } }
然后我就可以这么优雅地使用事务了
我再列出我的缓存拦截器,(感谢网友的提醒,我做了一下修改,针对异步方法返回值的处理),对了,下面的ICacheHelper是我定义的一个缓存助手接口,用的是redis,我会在后面写一篇博客
public class CacheInterceptorAttribute : AbstractInterceptorAttribute { /// <summary> /// 缓存秒数 /// </summary> public int ExpireSeconds { get; set; } public async override Task Invoke(AspectContext context, AspectDelegate next) { //判断是否是异步方法 bool isAsync = context.IsAsync(); //if (context.ImplementationMethod.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null) //{ // isAsync = true; //} //先判断方法是否有返回值,无就不进行缓存判断 var methodReturnType = context.GetReturnParameter().Type; if (methodReturnType == typeof(void) || methodReturnType == typeof(Task) || methodReturnType == typeof(ValueTask)) { await next(context); return; } var returnType = methodReturnType; if (isAsync) { //取得异步返回的类型 returnType = returnType.GenericTypeArguments.FirstOrDefault(); } //获取方法参数名 string param = CommonHelper.ObjectToJsonString(context.Parameters); //获取方法名称,也就是缓存key值 string key = "Methods:" + context.ImplementationMethod.DeclaringType.FullName + "." + context.ImplementationMethod.Name; var cache = context.ServiceProvider.GetService<ICacheHelper>(); //如果缓存有值,那就直接返回缓存值 if (cache.HashExists(key, param)) { //反射获取缓存值,相当于cache.HashGet<>(key,param) var value = typeof(ICacheHelper).GetMethod(nameof(ICacheHelper.HashGet)).MakeGenericMethod(returnType).Invoke(cache, new[] { key, param }); if (isAsync) { //判断是Task还是ValueTask if (methodReturnType == typeof(Task<>).MakeGenericType(returnType)) { //反射获取Task<>类型的返回值,相当于Task.FromResult(value) context.ReturnValue = typeof(Task).GetMethod(nameof(Task.FromResult)).MakeGenericMethod(returnType).Invoke(null, new[] { value }); } else if (methodReturnType == typeof(ValueTask<>).MakeGenericType(returnType)) { //反射构建ValueTask<>类型的返回值,相当于new ValueTask(value) context.ReturnValue = Activator.CreateInstance(typeof(ValueTask<>).MakeGenericType(returnType), value); } } else { context.ReturnValue = value; } return; } await next(context); object returnValue; if (isAsync) { returnValue = await context.UnwrapAsyncReturnValue(); //反射获取异步结果的值,相当于(context.ReturnValue as Task<>).Result //returnValue = typeof(Task<>).MakeGenericType(returnType).GetProperty(nameof(Task<object>.Result)).GetValue(context.ReturnValue); } else { returnValue = context.ReturnValue; } cache.HashSet(key, param, returnValue); if (ExpireSeconds > 0) { cache.SetExpire(key, TimeSpan.FromSeconds(ExpireSeconds)); } } }
我还弄了一个缓存删除拦截器,作用就是带有这个特性的方法执行后,会删除相关缓存值
为什么有这个设计呢,比如说我给一个方法 GetUserList 加了缓存,那我数据改变了怎么办,我想在User数据改变时,把这个缓存删除掉,那我就可以在SaveUser方法上加上我这个缓存删除拦截器,那这个方法执行后,就会把相关的缓存删除掉了
public class CacheDeleteInterceptorAttribute : AbstractInterceptorAttribute { private readonly Type[] _types; private readonly string[] _methods; /// <summary> /// 需传入相同数量的Types跟Methods,同样位置的Type跟Method会组合成一个缓存key,进行删除 /// </summary> /// <param name="Types">传入要删除缓存的类</param> /// <param name="Methods">传入要删除缓存的方法名称,必须与Types数组对应</param> public CacheDeleteInterceptorAttribute(Type[] Types, string[] Methods) { if (Types.Length != Methods.Length) { throw new ApiFailException(ApiFailCode.OPERATION_FAIL, "Types必须跟Methods数量一致"); } _types = Types; _methods = Methods; } public async override Task Invoke(AspectContext context, AspectDelegate next) { var cache = context.ServiceProvider.GetService<ICacheHelper>(); await next(context); for (int i = 0; i < _types.Length; i++) { var type = _types[i]; var method = _methods[i]; string key = "Methods:" + type.FullName + "." + method; cache.Delete(key); } } }
AOP的实现原理我也想象了一下:
要实现AOP,需要依靠IOC容器,因为它是我们类的管家,那能被拦截的类必须是IOC注入的,自己new出来的是不受拦截的。如果我想在A方法前面添加点代码,那我告诉IOC,把代码给它,那IOC在注入A方法所在类时,会继承它生成一个派生类,然后重写A方法,所以拦截方法必须得为virtual,然后A方法里写上我要添加的代码,再base.A()这样。
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新日志
- 模拟之声慢刻CD《柏林之声4》[正版CD低速原抓WAV+CUE]
- 李宗盛 《李宗盛经典金曲》[WAV+CUE][1G]
- 周华健《粤语精选》[WAV+CUE][1G]
- 蔡婧2024《天空》HQCDII头版限量编号[WAV+CUE][1G]
- 陈奂仁.2011-谁是陈奂仁【BBS】【FLAC分轨】
- 群星.2024-幻乐森林影视原声带【韶愔音乐】【FLAC分轨】
- 黎明.1999-向往金色的黎明新歌+精选2CD【环球】【WAV+CUE】
- 发烧女声Méav《美芙的祈祷》发烧女声 [WAV+CUE][820M]
- 雷婷 《我的爱回不来》紫银合金AQCD [WAV+CUE][1G]
- 群星 DTS《天籁之音·唱醉了草原》2CD[WAV分轨][1.6G]
- 魔兽世界wlk毁灭术一键输出宏是什么 wlk毁灭术一键输出宏介绍
- 三国志8重制版无法与武将交流怎么办 无法与武将交流解决方法一览
- 三国志8重制版恶名怎么消除 恶名影响与消除方法介绍
- 模拟之声慢刻CD《柏林之声5》2019[原抓WAV+CUE]
- AlexandraSoumm-Parisestunefte(2024)[24Bit-96kHz]FLAC