.net Core 中使用AspectCore实现AOP,实现事务、缓存拦截器

  

.net Core 中使用AspectCore实现AOP,实现事务、缓存拦截器

  最近想给我的框架加一种功能,就是比如给一个方法加一个事务的特性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()();

  }

  public static IHostBuilder CreateHostBuilder(string[] args)=>

  Host.CreateDefaultBuilder(args)

  .ConfigureWebHostDefaults(webBuilder=>

  {

  webBuilder.UseStartup();

  var dependAssemblyKey=BuildDependAssemblyKey();

  if (!string.IsNullOrEmpty(dependAssemblyKey))

  {

  webBuilder.UseSetting(WebHostDefaultsingStartupAssembliesKey, dependAssemblyKey);

  }

  })

  //用AspectCore替换默认的IOC容器

  .UseServiceProviderFactory(new DynamicProxyServiceProviderFactory());

  }

  然后在Startup.cs类中的ConfigureServices中添加代码。(其实这个加不加都可以,如果需要配置就加,例如全局的拦截器、只拦截哪些匹配的服务,因为我只用特性进行拦截,所以我就什么也没配置)

  services.ConfigureDynamicProxy(o=> {

  //添加AOP的配置

  });

  这样AOP就配置好了,是不是很简单。

  当然使用方面也需要注意一下,可以在接口、接口的方法、类,类的virtual方法上进行拦截。还有如果你想拦截控制器的action的话,那需要在ConfigureService里AddControllerAsServices

  services.AddControllers()

  //把控制器当成服务

  .AddControllersAsServices()

  下面我列出我的事务拦截器代码,如果是特性拦截,就继承

  AbstractInterceptorAttribute,如果要写一个全局拦截器,就AbstractInterceptor,然后在ConfigureDynamicProxy中进行配置,这个我就不介绍了

  public class TransactionInterceptorAttribute : AbstractInterceptorAttribute

  {

  public async override Task Invoke(AspectContext context, AspectDelegate next)

  {

  var dbContext=context.ServiceProvider.GetService();

  //先判断是否已经启用了事务

  if (dbContext.Database.CurrentTransaction==null)

  {

  await dbContext.Database.BeginTransactionAsync();

  try

  {

  await next(context);

  dbContext.DatabasemitTransaction();

  }

  catch (Exception ex)

  {

  dbContext.Database.RollbackTransaction();

  throw ex;

  }

  }

  else

  {

  await next(context);

  }

  }

  }

  然后我就可以这么优雅地使用食物了

  

.net Core 中使用AspectCore实现AOP,实现事务、缓存拦截器

  我再列出我的缓存拦截器,(感谢网友的提醒,我做了一下修改,针对异步方法返回值的处理),对了,下面的ICacheHelper是我定义的一个缓存助手接口,推荐用的是redis,我会在后面写一篇博客

  public class CacheInterceptorAttribute : AbstractInterceptorAttribute

  {

  ///

  /// 缓存秒数

  ///

  public int ExpireSeconds { get; set; }

  public async override Task Invoke(AspectContext context, AspectDelegate next)

  {

  bool isAsync=false;

  //判断是否是异步方法

  if (contextplementationMethod.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) !=null)

  {

  isAsync=true;

  }

  //先判断方法是否有返回值,无就不进行缓存判断

  var returnType=context.GetReturnParameter().Type;

  if (returnType==typeof(void) || returnType==typeof(Task))

  {

  await next(context);

  return;

  }

  if (isAsync)

  {

  //取得异步返回的类型

  returnType=returnType.GenericTypeArguments.FirstOrDefault();

  }

  //获取方法参数名

  string param=CommonHelper.ObjectToJsonString(context.Parameters);

  //获取方法名称,也就是缓存key值

  string key="Methods:" + contextplementationMethod.DeclaringType.FullName + "." + contextplementationMethod.Name;

  var cache=context.ServiceProvider.GetService();

  //如果缓存有值,那就直接返回缓存值

  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<>类型的返回值,相当于Task.FromResult(value)

  context.ReturnValue=typeof(Task).GetMethod(nameof(Task.FromResult)).MakeGenericMethod(returnType).Invoke(null, new[] { value });

  }

  else

  {

  context.ReturnValue=value;

  }

  return;

  }

  await next(context);

  object returnValue;

  if (isAsync)

  {

  //反射获取异步结果的值,相当于(context.ReturnValue as Task<>).Result

  returnValue=typeof(Task<>).MakeGenericType(returnType).GetProperty(nameof(Task.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;

  ///

  /// 需传入相同数量的Types跟Methods,同样位置的Type跟Method会组合成一个缓存key,进行删除

  ///

  /// 传入要删除缓存的类

  /// 传入要删除缓存的方法名称,必须与Types数组对应

  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();

  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()这样。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值