Asp .Net Core 系列:基于 Castle DynamicProxy + Autofac 实践 AOP 以及实现事务、用户填充功能

什么是 AOP ?

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在通过将横切关注点(cross-cutting concerns)从主要业务逻辑中分离出来,以提高代码的模块化性、可维护性和复用性。

在传统的面向对象编程中,我们通常通过类和对象来组织和实现功能。然而,某些功能,如日志记录、事务管理、安全性检查等,可能会跨越多个对象和模块,这种跨越称为横切关注点。AOP 的核心思想是将这些横切关注点从业务逻辑中分离出来,通过特定的机制将它们应用到代码中,而不是通过直接修改业务逻辑来实现。

.Net Core 中 有哪些 AOP 框架?

PostSharp(收费)

PostSharp是一个功能强大的AOP框架,它通过编译器插件的形式集成到Visual Studio中。PostSharp支持编译时AOP(通过C#特性应用切面),并提供了丰富的切面类型,包括方法拦截、属性访问拦截、异常处理等。它还提供了商业支持和丰富的文档。

Castle DynamicProxy

Castle DynamicProxy是Castle项目的一部分,它允许开发者在运行时动态创建代理类,这些代理类可以拦截对目标对象的调用,并在调用前后执行自定义逻辑。虽然它本身不是一个完整的AOP框架,但它经常被用作构建AOP解决方案的基础。

AspectCore Framework

AspectCore 是一个开源的 AOP 框架,专为 .NET Core 设计。它提供了基于动态代理的运行时切面和方法拦截机制,支持常见的切面编程需求,如日志、缓存、事务等。

基于 Castle DynamicProxy 实现 AOP

1. 安装Castle.Core NuGet包

Install-Package Castle.Core 

2. 定义接口和类

假设你有一个接口和一个实现了该接口的类,你想要拦截这个类的方法调用。

public interface IMyService   {       void PerformAction();   }    public class MyService : IMyService   {       public void PerformAction()       {           Console.WriteLine("Action performed.");       }   } 

3. 创建拦截器

接下来,你需要创建一个拦截器类,该类将实现IInterceptor接口。在这个接口的实现中,你可以定义在调用目标方法之前和之后要执行的逻辑。

using Castle.DynamicProxy;      public class MyInterceptor : IInterceptor   {       public void Intercept(IInvocation invocation)       {           // 在调用目标方法之前执行的逻辑           Console.WriteLine("Before method: " + invocation.Method.Name);              // 调用目标方法           invocation.Proceed();              // 在调用目标方法之后执行的逻辑           Console.WriteLine("After method: " + invocation.Method.Name);       }   } 

4. 创建代理并调用方法

最后,你需要使用ProxyGenerator类来创建MyService的代理实例,并指定拦截器。然后,你可以像使用普通对象一样调用代理的方法,但拦截器中的逻辑会在调用发生时执行。

using Castle.DynamicProxy;      public class Program   {       public static void Main(string[] args)       {           var generator = new ProxyGenerator();           var interceptor = new MyInterceptor();              // 创建MyService的代理实例,并指定拦截器           var proxy = generator.CreateInterfaceProxyWithTarget<IMyService>(               new MyService(), interceptor);              // 调用代理的方法,拦截器中的逻辑将被执行           proxy.PerformAction();       }   } 

注意,上面的示例使用了接口代理(CreateInterfaceProxyWithTarget),这意味着你的目标类必须实现一个或多个接口。如果你想要代理一个类而不是接口,你可以使用CreateClassProxyWithTarget方法(但这通常用于需要代理非虚方法或字段的场景,且要求目标类是可继承的)。

IOC中使用 Castle DynamicProxy

由于IOC容器(如Microsoft的IServiceCollectionIServiceProvider)通常不直接支持AOP,所以用 Autofac
1. 安装必要的 NuGet 包

首先,确保你的项目中安装了以下 NuGet 包:

Install-Package Autofac Install-Package Autofac.Extensions.DependencyInjection Install-Package Autofac.Extras.DynamicProxy Install-Package Castle.Core 

2. 创建服务接口和实现类

    public class User     {         public long Id { get; set; }          public string Name { get; set; }          public long CreateUserId { get; set; }          public string CreateUserName { get; set; }          public DateTime CreateTime { get; set; }          public long UpdateUserId { get; set; }          public string UpdateUserName { get; set; }          public DateTime UpdateTime { get; set; }     }          public interface IUserService     {         void Test();          Task<int> TaskTest();          void Add(User user);         void Update(User user);     }       public class UserService : IUserService     {         public void Test()         {             Console.WriteLine("Test");         }          public async Task<int> TaskTest()         {             await Console.Out.WriteLineAsync("TaskTest");             return 1;         }          public void Add(User user)         {             Console.WriteLine(user.CreateUserId);             Console.WriteLine(user.CreateUserName);         }          public void Update(User user)         {             Console.WriteLine(user.UpdateUserId);             Console.WriteLine(user.UpdateUserName);         }     }      [ApiController]     [Route("[controller]")]     public class UserController : ControllerBase     {         readonly IUserService _userService;          public UserController(IUserService userService)         {             _userService = userService;         }                  [HttpGet]         [Route("/taskTest")]         public async Task<string> TaskTest()         {             await _userService.TaskTest();             return "ok";         }          [HttpGet]         [Route("/test")]         public string Test()         {             _userService.Test();             return "ok";         }          [HttpGet]         [Route("/add")]         public string Add()         {             _userService.Add(new Model.User { Name = "张三" });             return "ok";         }           [HttpGet]         [Route("/update")]         public string Update()         {             _userService.Update(new Model.User { Name = "张三" });             return "ok";         }     } 

3. 创建拦截器类

创建一个实现 IInterceptor 接口的拦截器类 LoggingInterceptor,用于拦截方法调用并添加日志记录:

public class LoggingInterceptor : IInterceptor {     public void Intercept(IInvocation invocation)     {         Console.WriteLine($"Before executing: {invocation.Method.Name}");          invocation.Proceed(); // 调用原始方法          Console.WriteLine($"After executing: {invocation.Method.Name}");     } } 

4. 配置 Autofac 容器

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()) //使用Autofac                         .ConfigureContainer<ContainerBuilder>(autofacBuilder =>                         {                                                     autofacBuilder.RegisterType<LoggingInterceptor>();                                                                           autofacBuilder.RegisterType<UserService>().As<IUserService>    ().SingleInstance().AsImplementedInterfaces()                                 .EnableInterfaceInterceptors() // 启用接口拦截器                             .InterceptedBy(typeof(LoggingInterceptor)); //指定拦截器                         }); 

与Autofac集成时,配置拦截器主要有两种方式

使用 InterceptAttribute 特性

这种方式通过在接口或类上添加[Intercept(typeof(YourInterceptor))]特性来指定拦截器。然后,在Autofac注册时,启用接口或类的拦截器。(通常不推荐在类上直接添加,因为这会使类与Autofac紧密耦合)

 [Intercept(typeof(UserAutoFillInterceptor))]  public class UserService : IUserService {        public void Test()      {         Console.WriteLine("Test");      }  }  autofacBuilder.RegisterType<UserService>().As<IUserService>().EnableInterfaceInterceptors() // 启用接口拦截器 

使用 InterceptedBy() 方法

这种方式不依赖于[Intercept]特性,而是在注册服务时直接使用InterceptedBy()方法来指定拦截器。

                            autofacBuilder.RegisterType<UserService>().As<IUserService>()                                 .EnableInterfaceInterceptors() // 启用接口拦截器                             .InterceptedBy(typeof(LoggingInterceptor)); //指定拦截器 

实现事务管理

拦截器基类

    /// <summary>     /// 拦截基类     /// </summary>     /// <typeparam name="T"></typeparam>     public abstract class BaseInterceptor<T> : IInterceptor     {         protected readonly ILogger<T> _logger;         public BaseInterceptor(ILogger<T> logger)         {             _logger = logger;         }          /// <summary>         /// 拦截方法         /// </summary>         /// <param name="invocation"></param>         public virtual void Intercept(IInvocation invocation)         {             try             {                 Method = invocation.MethodInvocationTarget ?? invocation.Method;                 InterceptHandle(invocation);             }             catch (Exception ex)             {                 _logger.LogError(ex, ex.Message);                 throw ex;             }                  }          /// <summary>         /// 拦截处理         /// </summary>         /// <param name="invocation"></param>         public abstract void InterceptHandle(IInvocation invocation);          protected MethodInfo Method{ get; set; }          public static bool IsAsyncMethod(MethodInfo method)         {             return (method.ReturnType == typeof(Task) ||                 (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))             );         } } 

事务特性:用来判断是否需要事务管理的

    /// <summary>     /// 事务特性     /// </summary>     [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]     public class TransactionalAttribute : Attribute     {         public TransactionalAttribute()         {             Timeout = 60;         }          /// <summary>         ///          /// </summary>         public int Timeout { get; set; }          /// <summary>         /// 事务隔离级别         /// </summary>         public IsolationLevel IsolationLevel { get; set; }          /// <summary>         /// 事务传播方式         /// </summary>         public Propagation Propagation { get; set; }     }      /// <summary>     /// 事务传播方式     /// </summary>     public enum Propagation     {         /// <summary>         /// 默认:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中。         /// </summary>         Required = 0,          /// <summary>         /// 使用当前事务,如果没有当前事务,就抛出异常         /// </summary>         Mandatory = 1,          /// <summary>         /// 以嵌套事务方式执行         /// </summary>         Nested = 2,     } 

事务拦截器:处理事务的

    /// <summary>     /// 事务拦截器     /// </summary>     public class TransactionalInterceptor : BaseInterceptor<TransactionalInterceptor>     {         public TransactionalInterceptor(ILogger<TransactionalInterceptor> logger) : base(logger)         {          }          public override void InterceptHandle(IInvocation invocation)         {                          if (Method.GetCustomAttribute<TransactionalAttribute>(true) == null && Method.DeclaringType?.GetCustomAttribute<TransactionalAttribute>(true) == null)             {                 invocation.Proceed();             }             else             {                 try                 {                     Console.WriteLine("开启事务");                      //执行方法                     invocation.Proceed();                      // 异步获取异常,先执行                     if (IsAsyncMethod(invocation.Method))                     {                         var result = invocation.ReturnValue;                         if (result is Task)                         {                             Task.WaitAll(result as Task);                         }                     }                      Console.WriteLine("提交事务");                 }                 catch (Exception ex)                 {                     Console.WriteLine("回滚事务");                     _logger.LogError(ex, ex.Message);                     throw ex;                 }             }         }     } 

接口上加入事务特性

    //[Transactional]     public class UserService : IUserService     {          [Transactional]         public void Test()         {             Console.WriteLine("Test");         }     } 

注入IOC

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())                         .ConfigureContainer<ContainerBuilder>(autofacBuilder =>                         {                             autofacBuilder.RegisterType<TransactionalInterceptor>();                                                                   autofacBuilder.RegisterType<UserService>().As<IUserService>().SingleInstance().AsImplementedInterfaces()                             .EnableInterfaceInterceptors()                             .InterceptedBy(typeof(TransactionalInterceptor));                         }); 

测试

Asp .Net Core 系列:基于 Castle DynamicProxy + Autofac 实践 AOP 以及实现事务、用户填充功能

实现用户自动填充

上下户用户

    public interface IHttpContextUser     {          long UserId { get; }           string UserName { get;}     }       public class HttpContextUser : IHttpContextUser     {         private readonly IHttpContextAccessor _accessor;         public HttpContextUser(IHttpContextAccessor accessor)         {             _accessor = accessor;         }         public long UserId         {             get             {                 return 1; //这里暂时是写死的                 if (int.TryParse(_accessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Sid), out var userId))                 {                     return userId;                 }                 return default;             }         }          public string UserName         {             get             {                 return "admin"; //这里暂时是写死的                 return _accessor.HttpContext?.User?.FindFirstValue(ClaimTypes.Name) ?? "";             }         }     }   

注入IOC

           builder.Services.AddHttpContextAccessor();            builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())                         .ConfigureContainer<ContainerBuilder>(autofacBuilder =>                         {                                                     autofacBuilder.RegisterType<HttpContextUser>().As<IHttpContextUser>().SingleInstance().AsImplementedInterfaces();                             autofacBuilder.RegisterType<UserAutoFillInterceptor>();                                                                             autofacBuilder.RegisterType<UserService>().As<IUserService>().SingleInstance().AsImplementedInterfaces()                             .EnableInterfaceInterceptors()                             .InterceptedBy(typeof(UserAutoFillInterceptor));                         }); 

用户自动拦截器:处理用户填充的

    /// <summary>     /// 用户自动填充拦截器     /// </summary>     public class UserAutoFillInterceptor : BaseInterceptor<UserAutoFillInterceptor>     {         private readonly IHttpContextUser _user;         public UserAutoFillInterceptor(ILogger<UserAutoFillInterceptor> logger,IHttpContextUser user) : base(logger)         {             _user = user;         }          public override void InterceptHandle(IInvocation invocation)         {             //对当前方法的特性验证             if (Method.Name?.ToLower() == "add" || Method.Name?.ToLower() == "update")             {                 if (invocation.Arguments.Length == 1 && invocation.Arguments[0].GetType().IsClass)                 {                     dynamic argModel = invocation.Arguments[0];                     var getType = argModel.GetType();                     if (Method.Name?.ToLower() == "add")                     {                         if (getType.GetProperty("CreateUserId") != null)                         {                             argModel.CreateUserId = _user.UserId;                         }                         if (getType.GetProperty("CreateUserName") != null)                         {                             argModel.CreateUserName = _user.UserName;                         }                         if (getType.GetProperty("CreateTime") != null)                         {                             argModel.CreateTime = DateTime.Now;                         }                                           }                     if (getType.GetProperty("UpdateUserId") != null)                     {                         argModel.UpdateUserId = _user.UserId;                     }                     if (getType.GetProperty("UpdateUserName") != null)                     {                         argModel.UpdateUserName = _user.UserName;                     }                     if (getType.GetProperty("UpdateTime") != null)                     {                         argModel.UpdateTime = DateTime.Now;                     }                                         }                 invocation.Proceed();             }             else             {                 invocation.Proceed();             }         }     } 

测试

Asp .Net Core 系列:基于 Castle DynamicProxy + Autofac 实践 AOP 以及实现事务、用户填充功能

发表评论

评论已关闭。

相关文章