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

文章目录

    • 什么是 AOP ?
    • .Net Core 中 有哪些 AOP 框架?
    • 基于 Castle DynamicProxy 实现 AOP
    • IOC中使用 Castle DynamicProxy
    • 实现事务管理
    • 实现用户自动填充

什么是 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));});

测试

image

实现用户自动填充

上下户用户

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

测试

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/40724.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像的反转

图像颜色的反转一般分为两种&#xff1a;一种是灰度图片的颜色反转&#xff0c;另一种是彩色图像的颜色反转。 本节使用的原图如下&#xff1a; 1.1 灰度图像颜色反转 灰度图像每个像素点只有一个像素值来表示&#xff0c;色彩范围在0-255之间&#xff0c;反转方法255-当前像…

8.ApplicationContext常见实现

ClassPathXmlApplicationContext 基于classpath下xml格式的配置文件来创建 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-i…

Flutter——最详细(Drawer)使用教程

背景 应用左侧或右侧导航面板&#xff1b; 属性作用elevation相当于阴影的大小 import package:flutter/material.dart;class CustomDrawer extends StatelessWidget {const CustomDrawer({Key? key}) : super(key: key);overrideWidget build(BuildContext context) {return…

解决SeaTunnel 2.3.4版本写入S3文件报错问题

在使用Apache SeaTunnel时&#xff0c;我遇到了一个写入S3文件的报错问题。通过深入调试和分析&#xff0c;找到了问题所在&#xff0c;并提出了相应的解决方案。 本文将详细介绍报错情况、参考资料、解决思路以及后续研究方向&#xff0c;希望对大家有帮助&#xff01; 一、…

修改头文件版本需要修改的文件

以修改ui的头文件版本为例&#xff0c;还需要同时更新 PJ10PC20240120041_c928\components\master-t5\hikauto\module\app\include PJ10PC20240120041_c928\components\master-t5\hikauto\module\app\include\dsp PJ10PC20240120041_c928\components\master-t5\hikauto\incl…

【算法】(C语言):堆排序

堆&#xff08;二叉树的应用&#xff09;&#xff1a; 完全二叉树。最大堆&#xff1a;每个节点比子树所有节点的数值都大&#xff0c;根节点是最大值。父子索引号关系&#xff08;根节点为0&#xff09;&#xff1a;&#xff08;向上&#xff09;子节点x&#xff0c;父节点(x…

datawhale大模型应用开发夏令营学习笔记一

参考自 基于LangChainLLM的本地知识库问答&#xff1a;从企业单文档问答到批量文档问答datawhale的llm-universe 作者现在在datawhale夏令营的大模型应用开发这个班中&#xff0c;作为一个小白&#xff0c;为了能为团队做出一点贡献&#xff0c;现在就要开始学习怎么使用langch…

实战教程:如何用JavaScript构建一个功能强大的音乐播放器,兼容本地与在线资源

项目地址&#xff1a;Music Player App 作者&#xff1a;Reza Mehdikhanlou 视频地址&#xff1a;youtube 我将向您展示如何使用 javascript 编写音乐播放器。我们创建一个项目&#xff0c;您可以使用 javascript 从本地文件夹或任何 url 播放音频文件。 项目目录 assets 1…

顶级10大AI测试工具

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

JWT入门

JWT与TOKEN JWT&#xff08;JSON Web Token&#xff09;是一种基于 JSON 格式的轻量级安全令牌&#xff0c;通常用于在网络应用间安全地传递信息。而“token”一词则是一个更广泛的术语&#xff0c;用来指代任何形式的令牌&#xff0c;用于在计算机系统中进行身份验证或授权。J…

【️讲解下Laravel为什么会成为最优雅的PHP框架?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

cloudreve 设置开机服务

创建一个Systemd服务文件&#xff1a; 打开终端并创建一个新的服务文件&#xff1a; sudo nano /etc/systemd/system/cloudreve.service 在服务文件中添加以下内容&#xff1a; 根据你的设置调整路径和参数&#xff0c;然后将以下配置粘贴到文件中&#xff1a; [Unit] Descri…

Django学习第四天

启动项目命令 python manage.py runserver 分页功能封装到类中去 封装的类的代码 """ 自定义的分页组件,以后如果想要使用这个分页组件&#xff0c;你需要做&#xff1a; def pretty_list(request):# 靓号列表data_dict {}search_data request.GET.get(q, &…

Excel为数据绘制拆线图,并将均值线叠加在图上,以及整个过程的区域录屏python脚本

Excel为数据绘制拆线图,并将均值线叠加在图上,以及整个过程的区域录屏python脚本 1.演示动画A.视频B.gif动画 2.跟踪鼠标区域的录屏脚本 Excel中有一组数据,希望画出曲线,并且能把均值线也绘制在图上,以下动画演示了整个过程,并且提供了区域录屏脚本,原理如下: 为节约空间,避免…

从华为和特斯拉之争,看智能驾驶的未来

“一旦特斯拉完全解决自动驾驶问题并量产Optimus&#xff0c;任何空头都将被消灭&#xff0c;即使是比尔-盖茨也不例外。”7月2日&#xff0c;马斯克再次在社交媒体X上画下了这样的“大饼”。 与此同时&#xff0c;特斯拉的股价在最近的三个交易日也迎来了24%的涨幅&#xff0c…

中俄汽车产业链合作前景广阔,东方经济论坛助力双边合作与创新

随着中国汽车零部件企业的竞争力和创新能力不断增强&#xff0c;中国汽车及零部件行业在俄罗斯的市场份额和品牌影响力显著提升&#xff0c;中俄两国在汽车产业链上的合作展现出巨大的潜力和广阔的前景。2024年5月&#xff0c;俄罗斯乘用车新车销量达到12.8万辆&#xff0c;同比…

7.基于SpringBoot的SSMP整合案例-表现层开发

目录 1.基于Restfu1进行表现层接口开发 1.1创建功能类 1.2基于Restful制作表现层接口 2.接收参数 2使用Apifox测试表现层接口功能 保存接口&#xff1a; 分页接口&#xff1a; 3.表现层一致性处理 3.1先创建一个工具类&#xff0c;用作后端返回格式统一类&#xff1a;…

SpringMVC 的工作流程和详细解释

Spring MVC&#xff08;Model-View-Controller&#xff09;框架是基于经典的 MVC 设计模式构建的&#xff0c;用于开发 Web 应用程序。下面是 Spring Boot MVC 的工作流程和详细解释&#xff1a; 1.客户端发起请求 1.客户端&#xff08;通常是浏览器&#xff09;发起 HTTP 请求…

Python学习篇:Python基础知识(三)

目录 1 Python保留字 2 注释 3 行与缩进 ​编辑4 多行语句 5 输入和输出 6 变量 7 数据类型 8 类型转换 9 表达式 10 运算符 1 Python保留字 Python保留字&#xff08;也称为关键字&#xff09;是Python编程语言中预定义的、具有特殊含义的标识符。这些保留字不能用作…

代码随想录算法训练营第70天图论9[1]

代码随想录算法训练营第70天:图论9 ‍ 拓扑排序精讲 卡码网&#xff1a;117. 软件构建(opens new window) 题目描述&#xff1a; 某个大型软件项目的构建系统拥有 N 个文件&#xff0c;文件编号从 0 到 N - 1&#xff0c;在这些文件中&#xff0c;某些文件依赖于其他文件的…