ASP.NET Core基础之AOP编程(二)-拦截器

阅读本文你的收获:

  1. 学习AOP的另外一种实现方式——拦截器
  2. 了解拦截器和过滤器的区别
  3. 手把手教你写一个拦截器实现事务管理

引言

上一篇文章ASP.NET Core基础之AOP编程(一)-过滤器中,我们介绍了AOP思想的其中一种实现方式——过滤器。这次我们来学习另外一种实现AOP思想的方式:拦截器。

一. 拦截器(Interceptors)的应用场景?

应用场景有:

  • 拦截和修改请求和响应数据
  • 限制请求速率和并发连接数
  • 实现缓存和本地存储
  • 在请求处理前进行数据验证和格式化
  • 在请求处理后进行数据处理和清理
  • 数据库事务处理

过滤器和拦截器的区别:

  • 过滤器是对控制器Controller/Action的AOP的实现;
  • 拦截器,拦截的是注册在IOC容器里的服务实例里的方法;
  • 过滤器和拦截器 底层实现方式大不相同,过滤器是基于函数回调的,拦截器则是基于动态代理机制实现的。

二. Autofac与拦截器

Autofac是一个.NET下非常优秀,性能非常好的IOC容器(.NET下效率最高的容器),加上AOP简直是如虎添翼。

Autofac的AOP是通过大名鼎鼎的Castle(也是一个容器)项目的Castle.Core来实现的,名为Autofac.Extras.DynamicProxy,顾名思义,其实现方式为动态代理(DynamicProxy)。

什么是动态代理?

动态代理是代理模式的一种实现方式,代理模式除了动态代理还有 静态代理,只不过静态代理能够在编译时期确定类的执行对象,而动态代理只有在运行时才能够确定执行对象是谁。代理可以看作是对最终调用目标的一个封装,我们能够通过操作代理对象来调用目标类,这样就可以实现调用者和目标对象的解耦合

一般我们获取一个类型的实例都是通过 new 关键字,例如 var c = new Class1(); 通过动态代理的话,我们获取一个实例是通过代理方法获取的,generator.CreateClassProxy(type, interceptor); 其中Type是要代理的类型,也就是Class1(); interceptor则是实际执行代理的拦截器。

三. 用拦截器实现AOP编程

以下演示用拦截器实现事务AOP,开发环境:

平台版本是:.NET6
开发框架:ASP.NET Core WebApi
开发工具:Visual Studio 2022

3.1 实现事务拦截器

  1. 编写自定义事务拦截器,在这之前我们先定义一个UseTranAttribute;

      /// <summary>
    /// 这个Attribute就是使用时候的验证,把它添加到需要执行事务的方法中,即可完成事务的操作。
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, Inherited = true)]
    public class UseTranAttribute : Attribute
    {
    }
    
     /// <summary>
     /// 数据库事务拦截器 继承Castle.DynamicProxy.IInterceptor接口
     /// </summary>
     public class DbTranInterceptor : IInterceptor
     {
         //记录日志
         private readonly ILogger<DbTranInterceptor> _logger;
    
         //工作单元对象
         private readonly IUnitOfWorkManage _unitOfWorkManage;
    
         public DbTranInterceptor(IUnitOfWorkManage unitOfWorkManage, ILogger<DbTranInterceptor> logger)
         {
             _unitOfWorkManage = unitOfWorkManage;
             _logger = logger;
         }
    
         /// <summary>
         /// 实例化IInterceptor唯一方法 
         /// </summary>
         /// <param name="invocation">包含被拦截方法的信息</param>
         public void Intercept(IInvocation invocation)
         {
             //获取被拦截的方法对象
             var method = invocation.MethodInvocationTarget ?? invocation.Method;
             //检查当前方法是否有[UseTran]特性修饰 
             if (method.GetCustomAttribute<UseTranAttribute>(true) is { } uta)
             {
                 try
                 {
                     _unitOfWorkManage.BeginTran(); //开启事务
    
                     invocation.Proceed();
    
                     //判断方法是否为异步方法,异步方法则需要Wait等待
                     if (IsAsyncMethod(invocation.Method))
                     {
                         var result = invocation.ReturnValue;
                         if (result is Task)
                         {
                             Task.WaitAll(result as Task);
                         }
                     }
    
                     _unitOfWorkManage.CommitTran();   //提交事务
                 }
                 catch (Exception ex)
                 {
                     _logger.LogError(ex.ToString());
                     _unitOfWorkManage.RollbackTran(); //回滚事务
                     throw;
                 }
             }
             else
             {
                 invocation.Proceed(); //如果方法没有[UseTran]则不需要开启事务,直接执行被拦截方法
             }
         }
    
         /// <summary>
         /// 判断方法是否为异步方法
         /// </summary>
         /// <param name="method"></param>
         /// <returns></returns>
         public static bool IsAsyncMethod(MethodInfo method)
         {
             return (
                 method.ReturnType == typeof(Task) ||
                 (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
             );
         }
     }
    
  2. 注册自定义事务拦截器
    在Program.cs中,启用Autofac依赖注入容器,并完成拦截器注册。

    using Autofac;
    using Autofac.Extensions.DependencyInjection;
    using Autofac.Extras.DynamicProxy;  //动态代理
    
    builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
        .ConfigureContainer<ContainerBuilder>(builder =>
        {
            //注册拦截器到Autofac容器
            builder.RegisterType<DbTranInterceptor>();
    
            //AutoFac批量注册所有Service类,并启用拦截器
            builder.RegisterAssemblyTypes(Assembly.Load("OAWeb.Service"))
                .Where(t => t.Name.EndsWith("Service"))
                .AsImplementedInterfaces()
                .PropertiesAutowired()
                .EnableInterfaceInterceptors()            //这里动态创建一个接口代理,另外还有一个
                .InterceptedBy(typeof(DbTranInterceptor)); //动态注入拦截器DbTranInterceptor
    
            //AutoFac批量注册所有Repository仓储类    
            builder.RegisterAssemblyTypes(Assembly.Load("OAWeb.EFCore"))
                .Where(t => t.Name.EndsWith("Repository"))
                .AsImplementedInterfaces()
                .PropertiesAutowired()
                .InstancePerLifetimeScope();  //域模式
    
            //AutoFac批量注册泛型仓储
            builder.RegisterGeneric(typeof(EfBaseRepository<>))
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope();
    
        });
    
  3. 测试自定义事务拦截器
    要在服务层的方法上边加上[UseTran],则该方法中的代码会在一个事务中执行。

    public class UserService : IUserService
    {
    	//完成必要的一些服务注入,略
    	
    	//为用户添加角色
    	[UseTran] //使用事务的特性标注
    	public int AddRole(AddUserRoleDto input)
    	{
    		//为用户添加角色
    	}
    }
    

本次分享就这么多,AOP相关的基础入门到此篇就结束了,如果对你有帮助,欢迎点赞+评论+关注。

.NET 5 引入了新的过滤器和拦截器的概念来帮助我们在应用程序中实现更好的控制和可重用性。虽然它们的目的相似,但它们在实现上有一些差异。 过滤器是一种在应用程序中处理请求和响应的方式。它们可以在整个应用程序生命周期中应用,并且可以应用于控制器、动作和全局级别。过滤器可以用于处理身份验证、授权、异常处理等操作。过滤器可以通过实现特定的接口(例如 IActionFilter,IAuthorizationFilter 等)或继承抽象类(例如 ActionFilterAttribute,AuthorizeAttribute 等)来创建自定义过滤器。它们提供了在请求和响应的不同阶段中执行代码的机制。 拦截器是在方法调用前后进行拦截和处理的一种机制。它们主要用于 AOP(面向切面编程)的实现,并且可以应用于控制器、动作、服务和全局级别。与过滤器不同,拦截器只能用于方法调用。拦截器可以通过实现 IInterceptor 接口或继承抽象类(例如 InterceptorAttribute)来创建自定义拦截器。它们提供了在方法执行前后进行代码注入和处理的能力,并可以修改方法的输入参数和输出结果。 尽管过滤器和拦截器的目的相似,但它们在应用方式和适用范围上有所不同。过滤器更注重于请求和响应的处理,并且可以在全局范围内应用,而拦截器更注重于方法的拦截和处理,并且仅适用于方法调用。具体使用哪种方式取决于应用程序的需求和设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

采石之人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值