Asp.net Core 3.1基于AspectCore實現(xiàn)AOP實現(xiàn)事務(wù)、緩存攔截器功能
最近想給我的框架加一種功能,就是比如給一個方法加一個事務(wù)的特性Attribute,那這個方法就會啟用事務(wù)處理。給一個方法加一個緩存特性,那這個方法就會進行緩存。
這個也是網(wǎng)上說的面向切面編程AOP。
AOP的概念也很好理解,跟中間件差不多,說白了,就是我可以任意地在方法的前面或后面添加代碼,這很適合用于緩存、日志等處理。
在net core2.2時,我當時就嘗試過用autofac實現(xiàn)aop,但這次我不想用autofac,我用了一個更輕量級的框架,AspectCore。
用起來非常非常的簡單,但一開始還是走了一點彎路,主要是網(wǎng)上都是net core3以下的教程,3以下的使用方法跟之前有一些不同。
先安裝NuGet包,包名:AspectCore.Extensions.DependencyInjection
然后在Program.cs類中增加一行代碼,這是net core 3的不同之處,這句添加的代碼,意思就是用AspectCore的IOC容器替換內(nèi)置的。因為AOP需要依靠IOC實現(xiàn),所以必須得替換掉內(nèi)置的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中添加代碼。(其實這個加不加都可以,如果需要配置就加,例如全局的攔截器、只攔截哪些匹配的服務(wù),因為我只用特性進行攔截,所以我就什么也沒配置)
services.ConfigureDynamicProxy(o=> { //添加AOP的配置 });
這樣AOP就配置好了,是不是很簡單。
當然使用方面也需要注意一下,可以在接口、接口的方法、類,類的virtual方法上進行攔截。還有如果你想攔截控制器的action的話,那需要在ConfigureService里AddControllerAsServices
services.AddControllers() //把控制器當成服務(wù) .AddControllersAsServices()
下面我列出我的事務(wù)攔截器代碼,如果是特性攔截,就繼承AbstractInterceptorAttribute,如果要寫一個全局攔截器,就AbstractInterceptor,然后在ConfigureDynamicProxy中進行配置,這個我就不介紹了
如果你的攔截器是放在其他項目的,那要記得添加AspectCore.Core包,不要只添加AspectCore.Abstractions,我一開始就只添加了AspectCore.Abstractions,一直沒發(fā)現(xiàn)IsAsync、UnwrapAsyncReturnValue等一些擴展方法。
public class TransactionInterceptorAttribute : AbstractInterceptorAttribute { public async override Task Invoke(AspectContext context, AspectDelegate next) { var dbContext = context.ServiceProvider.GetService<AppDbContext>(); //先判斷是否已經(jīng)啟用了事務(wù) 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); } } }
然后我就可以這么優(yōu)雅地使用事務(wù)了
我再列出我的緩存攔截器,(感謝網(wǎng)友的提醒,我做了一下修改,針對異步方法返回值的處理),對了,下面的ICacheHelper是我定義的一個緩存助手接口,用的是redis,我會在后面寫一篇博客
public class CacheInterceptorAttribute : AbstractInterceptorAttribute { /// <summary> /// 緩存秒數(shù) /// </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(); } //獲取方法參數(shù)名 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)) { //反射構(gòu)建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(); //反射獲取異步結(jié)果的值,相當于(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)); } } }
我還弄了一個緩存刪除攔截器,作用就是帶有這個特性的方法執(zhí)行后,會刪除相關(guān)緩存值
為什么有這個設(shè)計呢,比如說我給一個方法 GetUserList 加了緩存,那我數(shù)據(jù)改變了怎么辦,我想在User數(shù)據(jù)改變時,把這個緩存刪除掉,那我就可以在SaveUser方法上加上我這個緩存刪除攔截器,那這個方法執(zhí)行后,就會把相關(guān)的緩存刪除掉了
public class CacheDeleteInterceptorAttribute : AbstractInterceptorAttribute { private readonly Type[] _types; private readonly string[] _methods; /// <summary> /// 需傳入相同數(shù)量的Types跟Methods,同樣位置的Type跟Method會組合成一個緩存key,進行刪除 /// </summary> /// <param name="Types">傳入要刪除緩存的類</param> /// <param name="Methods">傳入要刪除緩存的方法名稱,必須與Types數(shù)組對應(yīng)</param> public CacheDeleteInterceptorAttribute(Type[] Types, string[] Methods) { if (Types.Length != Methods.Length) { throw new ApiFailException(ApiFailCode.OPERATION_FAIL, "Types必須跟Methods數(shù)量一致"); } _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的實現(xiàn)原理我也想象了一下:
要實現(xiàn)AOP,需要依靠IOC容器,因為它是我們類的管家,那能被攔截的類必須是IOC注入的,自己new出來的是不受攔截的。如果我想在A方法前面添加點代碼,那我告訴IOC,把代碼給它,那IOC在注入A方法所在類時,會繼承它生成一個派生類,然后重寫A方法,所以攔截方法必須得為virtual,然后A方法里寫上我要添加的代碼,再base.A()這樣。
到此這篇關(guān)于Asp.net Core 3.1基于AspectCore實現(xiàn)AOP實現(xiàn)事務(wù)、緩存攔截器功能的文章就介紹到這了,更多相關(guān)Asp.net Core 3.1實現(xiàn)事務(wù)、緩存攔截器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ASP.NET?MVC5網(wǎng)站開發(fā)項目框架(二)
這篇文章主要介紹了ASP.NET?MVC5網(wǎng)站開發(fā)項目框架,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2015-09-09asp.net Google的translate工具翻譯 API
很久前的一天,我想使用這個東西,然后看了下,GooGle的Translate工具目前沒有公開API,還是一個逐漸完善的過程,另一方面,利用一段很小的程序就可以得到我們想要的效果。2008-12-12.NET實現(xiàn)在網(wǎng)頁中預(yù)覽Office文件的3個方法
這篇文章主要介紹了.NET實現(xiàn)在網(wǎng)頁中預(yù)覽Office文件的3個方法,本文最終采用了ASPOSE+pdf2swf+FlexPaper的方式解決了這個需求,需要的朋友可以參考下2014-10-10利用Asp.Net Core的MiddleWare思想如何處理復(fù)雜業(yè)務(wù)流程詳解
這篇文章主要給大家介紹了關(guān)于利用Asp.Net Core的MiddleWare思想如何處理復(fù)雜業(yè)務(wù)流程的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起看看吧2018-08-08ABP(現(xiàn)代ASP.NET樣板開發(fā)框架)系列之二、ABP入門教程詳解
ABP是為新的現(xiàn)代Web應(yīng)用程序使用最佳實踐和使用最流行工具的一個起點。可作為一般用途的應(yīng)用程序的基礎(chǔ)框架或項目模板。接下來通過本文給大家詳細介紹ABP入門教程,感興趣的朋友一起看看吧2017-10-10ASP.NET技巧:請求網(wǎng)址并解析返回的html
ASP.NET技巧:請求網(wǎng)址并解析返回的html...2006-09-09CHECKBOX 的全選、取消及跨頁保存的實現(xiàn)方法
CHECKBOX的操作在頁面中很常見,比如全選、取消、跨頁保存等等,下面有個不錯的示例,大家可以嘗試操作下2013-10-10