首页 > 编程语言 >.NET Core 3.x 基于AspectCore实现AOP,实现事务、缓存拦截器

.NET Core 3.x 基于AspectCore实现AOP,实现事务、缓存拦截器

时间:2024-06-19 23:31:44浏览次数:26  
标签:Core 缓存 key 拦截器 Task typeof context AOP public

最近想给我的框架加一种功能,就是比如给一个方法加一个事务的特性Attribute,那这个方法就会启用事务处理。给一个方法加一个缓存特性,那这个方法就会进行缓存。这个也是网上说的面向切面编程AOP。
AOP的概念也很好理解,跟中间件差不多,说白了,就是我可以任意地在方法的前面或后面添加代码,这很适合用于缓存、日志等处理。
在net core2.2时,我当时就尝试过用autofac实现aop,但这次我不想用autofac,我用了一个更轻量级的框架,AspectCore。

先安装NuGet包,包名:AspectCore.Extensions.DependencyInjection,然后在Program.cs类中增加一行代码,这是.NET Core 3.x的不同之处,这句添加的代码,意思就是用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的话,那需要在ConfigureServiceAddControllerAsServices

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);
		}
	}
}
PLAINTEXT 复制 全屏

总结:要实现AOP,需要依靠IOC容器,因为它是我们类的管家,那能被拦截的类必须是IOC注入的,自己new出来的是不受拦截的。如果我想在A方法前面添加点代码,那我告诉IOC,把代码给它,那IOC在注入A方法所在类时,会继承它生成一个派生类,然后重写A方法,所以拦截方法必须得为virtual,然后A方法里写上我要添加的代码,再base.A()这样。

标签:Core,缓存,key,拦截器,Task,typeof,context,AOP,public
From: https://www.cnblogs.com/chinasoft/p/18257782

相关文章

  • Microsoft.AspNetCore.Identity 的使用记录
    使用Cookie,在发起请求时从浏览器附加Cookie的示例图:请求后自动生成Cookie,缓存保存于浏览器中以后每次发起请求时浏览器都会自动为请求附加其缓存的Cookies使用访问令牌bearertoken,在发起请求时附加了Authorization请求头,示例注意,如果手动传参,Head中Authorizaion的值要......
  • 解决《模拟山羊》游戏启动问题:缺失wxmsw28u_core_vc_custom.dll的终极指南
    在沉浸于《模拟山羊》那荒诞不经、充满乐趣的游戏世界之前,你可能遭遇了一个小小的障碍——游戏启动时弹出错误提示,指出系统缺少一个名为“wxmsw28u_core_vc_custom.dll”的关键文件。别担心,这并非罕见问题,而且解决起来比你想象的要简单。本文将手把手教你如何跨越这个小障碍,确......
  • CorelDraw 2024软件安装包下载 丨不限速下载丨亲测好用
    ​简介:CorelDRAWGraphicsSuite订阅版拥有配备齐全的专业设计工具包,可以通过非常高的效率提供令人惊艳的矢量插图、布局、照片编辑和排版项目。价格实惠的订阅就能获得令人难以置信的持续价值,即时、有保障地获得独家的新功能和内容、一流的性能,以及对最新技术的支持。获得......
  • 最新.NET 8 中的 .NET Core 配置使用方法
    在这篇文章中,我将探讨.NET中配置的演变、框架如何处理配置,并提供实际示例来帮助你在应用程序中实现这些概念。什么是配置?.NET中的配置是指控制应用程序行为的设置。这些设置可以包括重试时间、队列长度、功能标志和机密(如连接字符串)等值。传统上,配置是在编译时应用的,但现......
  • Asp.net Core依赖注入(Autofac替换IOC容器)
    ASP.NETCoreASP.NETCore(previouslyASP.NET5)改变了以前依赖注入框架集成进ASP.NET的方法.以前,每个功能-MVC,WebAPI,等.-都有它自己的"依赖解析器(dependencyresolver)"机制并且只是'钩子'钩住的方式有些轻微的区别.ASP.NETCore通过Microsoft.Extensions.......
  • 事务&AOP
    事物管理事务管理是指对一系列数据库操作进行管理,确保这些操作要么全部成功执行,要么在遇到错误时全部回滚,以维护数据的一致性和完整性。在多用户并发操作和大数据处理的现代软件开发领域中,事务管理已成为确保数据一致性和完整性的关键技术之一。基本概念定义:事务是由N步数据......
  • 【Spring】AOP
    AOP英文全称:AspectOrientedProgramming(面向切面编程、面向方面编程)。在程序运行期间在不修改源代码的基础上对已有方法进行增强(无侵入性:解耦)AOP快速入门pom.xml<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</......
  • NetCore资料
    .NET项目中NLog的配置与使用     NETCore中的验证组件FluentValidation  Core数据校验:FluentValidation用法.NETCore学习资料精选:入门   教程:ASP.NETCore入门  C#/.NET/.NETCore推荐学习书籍  如何学习CORE  .netcore中使用Autofac    Autofa......
  • Net6 EFCore 基于MSSQL & T4 自动生成字段注释
    文件模板代码<#@templatelanguage="C#"#><#@outputextension=".cs"#><#@assemblyname="System.Core"#><#@importnamespace="System.IO"#><#@importnamespace="System.Linq"#>......
  • Angular项目简单使用拦截器 httpClient 请求响应处理
    1:为啥要使用拦截器httpClient请求响应处理,其作用我们主要是:目前我的Angular版本是Angular17.3,版本中实现请求和响应的拦截处理了。这种机制非常适合添加如身份验证头、错误统一处理、日志记录等功能。======具体的操作步骤=======2:注入服务:nggsservices/myhttp-intercept......