aaspnetcore開發框架中實現aop不僅僅在業務上,在程式碼的優雅簡潔和架構的穩定上都有著至關重要。
下面介紹三種用過的。
第一種通過System.Reflection的DispatchProxy類來實現
首先新建一個aspnetcore專案
針對業務程式碼WarService加了一個代理的方法
public interface IWarService { string WipeOut(); IWarService Proxy(IWarService warService); } public class WarService : IWarService { public IWarService Proxy(IWarService warService) { return WarDispatch<IWarService>.Create(warService); } public string WipeOut() { return "us is over"; } }
具體的WarDispatch就是核心程式碼了,繼承自DispatchProxy。這裡的before和after的實現就是針對實現了程式碼的service提前挖坑。
public class WarDispatch<T> : DispatchProxy where T : class { private T Target { get; set; } public static T Create<T>(T target) where T : class { var proxy = Create<T, WarDispatch<T>>() as WarDispatch<T>; proxy.Target = target; return proxy as T; } protected override object? Invoke(MethodInfo? targetMethod, object?[]? args) { Before().Wait(); var result = targetMethod.Invoke(Target, args); After().Wait(); return result; } Task Before() { return Task.CompletedTask; } Task After() { return Task.CompletedTask; } }
實現程式碼也相當簡單
[ApiController] [Route("[controller]")] public class RescueEarthController : ControllerBase { private IWarService _warService; public RescueEarthController(IWarService warService) { _warService = warService; } [HttpGet(Name = "AnnihilateHegemony")] public string AnnihilateHegemony() { var proxy = _warService.Proxy(_warService); //代理 return proxy.WipeOut(); } [HttpGet("two")] public string AnnihilateHegemonyTwo() { return _warService.WipeOut(); } }
當然不要忘了注入下服務類
builder.Services.AddScoped<IWarService, WarService>();
上面的方式是我自己想出來的,具體到專案中需要改進的地方應該還有很多,但是足夠簡單,功能也比較單一。
下面簡單介紹下AspectCore.DynamicProxy現成元件的代理使用。
首先參照aspnetcore.extensions.dependencyinjection包
在program中使用動態程式碼
builder.Host.UseServiceProviderFactory(new DynamicProxyServiceProviderFactory()); builder.Services.ConfigureDynamicProxy(o =>{ //新增aop的設定 //該專案用attribute所以無需設定 });
記憶體的快取代理
public class CacheDeleteInterceptorAttribute:AbstractInterceptorAttribute { private readonly Type[] _types; private readonly string[] _methods; public CacheDeleteInterceptorAttribute(Type[] types, string[] methods) { if (types.Length != methods.Length) { throw new Exception("Types必須跟Methods數量一致"); } _types = types; _methods = methods; } public override async Task Invoke(AspectContext context, AspectDelegate next) { var cache = context.ServiceProvider.GetService<MemoryCache>(); 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.Remove(key); } } }
public class CacheInterceptorAttribute : AbstractInterceptorAttribute { public override async Task Invoke(AspectContext context, AspectDelegate next) { bool isAsync = context.IsAsync(); 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 = GetParaName(context.Parameters); //獲取方法的引數名, string key = $"Methods:{context.ImplementationMethod.DeclaringType.FullName}.{context.ImplementationMethod.Name}";//獲取方法名稱,也就是快取key值 var cache = context.ServiceProvider.GetService<MemoryCache>(); //可以使用自定義的redis或者其他快取 if (cache.Get(key) != null) { //反射獲取快取值 var value = typeof(MemoryCache).GetMethod("MemoryCache.Get").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.Set(key //, param , returnValue); if(ExpireSeconds > 0) { cache.Set(key, TimeSpan.FromSeconds(ExpireSeconds));//設定key的過期時間 } } //private string GetParaName(object[] parameters) //{ // throw new NotImplementedException(); //} /// <summary> /// 快取秒數 /// </summary> public int ExpireSeconds { get; set; } }
dbcontext的代理
public class TransactionInterceptorAttribute : AbstractInterceptorAttribute { //public override async Task Invoke(AspectContext context, AspectDelegate next) //{ // var dbcontext = context.ServiceProvider.GetService<CommonDbContext>(); // if (dbcontext.Database.CurrentTransaction != null) // { // await dbcontext.Database.BeginTransactionAsync(); // try // { // await next(context); // await dbcontext.Database.CommitTransactionAsync(); // }catch(Exception ex) // { // await dbcontext.Database.RollbackTransactionAsync(); // throw ex; // } // } // else // { // await next(context); // } //}//一個context public override async Task Invoke(AspectContext context, AspectDelegate next) { var dbcontext = context.ServiceProvider.GetService<CommonDbContext>(); var dbcontextNext = context.ServiceProvider.GetService<NextDbContext>(); var transactionManager = dbcontext.Database.GetService<IDbContextTransactionManager>(); var transaction = await transactionManager.BeginTransactionAsync(); if (transaction != null) { await dbcontext.Database.BeginTransactionAsync(); try { await next(context); await transaction.CommitAsync(); } catch (Exception ex) { await transaction.RollbackAsync(); throw ex; } } else { await next(context); } }//多個context }
public class CommonDbContext:DbContext { public CommonDbContext(DbContextOptions<CommonDbContext> options):base(options) { } } public class NextDbContext : DbContext { public NextDbContext(DbContextOptions<CommonDbContext> options) : base(options) { } }
使用就是這麼簡單
public class TestOperatorDbBusiness { [TransactionInterceptor] public async ValueTask Add() { //TODO事務操作 } }
上面的代理元件功能非常多,專案中需要自己去研究更多更全的用法。
上面程式碼的demo
exercisebook/AOP at main · liuzhixin405/exercisebook (github.com)
還有Castle.DynamicProxy,這個比較複雜一點。具體用法給個範例demo
exercisebook/AspNetCoreAOP at main · liuzhixin405/exercisebook (github.com)
總結:
一個aspnetcore中需要用到aop的地方非常多,框架自帶的中介軟體,filter過濾器,efcore自帶Interceptor都可以拿來用。
中介軟體例如mediator,這裡面的攔截器也非常多,還有好多等待發掘。
當然自己也可以定義一些簡單的中間層來做攔截。
相信多瞭解 在框架中有需要用的地方會事半功倍。