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.rhkb.cn/news/368519.html

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

相关文章

TCP、UDP详解

目录 1.区别 1.1 概括 1.2 详解 2.TCP 2.1 内容 2.2 可靠传输 2.2.1 确认应答 2.2.2 超时重传 2.2.3 连接管理 三次握手 四次挥手 2.2.4 滑动窗口 2.2.5 流量控制 2.2.6 拥塞控制 2.2.7 延时应答 2.2.8 捎带应答 2.2.9 面向字节流 2.2.10 异常情况的处理 1.…

IDEA 编译单个Java文件

文章目录 一、class文件的生成位置二、编译单个文件编译项目报错Error:java: 无效的源发行版: 8 一、class文件的生成位置 file->project structure->Modules 二、编译单个文件 选中文件&#xff0c;点击recompile 编译项目报错 Error:java: 无效的源发行版: 8 Fi…

ffmpeg使用png编码器把rgb24编码为png图像

version #define LIBAVCODEC_VERSION_MAJOR 60 #define LIBAVCODEC_VERSION_MINOR 15 #define LIBAVCODEC_VERSION_MICRO 100 note 不使用AVOutputFormat code void CFfmpegOps::EncodeRGB24ToPNG(const char *infile, const char *width_str, const char *height_str…

海外短剧CPS推广分佣系统平台讲解,他和短剧播放平台有啥区别?

首先来讲讲什么是海外短剧系统&#xff1f;什么是海外短剧cps系统&#xff1f;这俩有何区别&#xff1f; 海外短剧系统 顾名思义&#xff1a;就是做一套海外短剧系统&#xff0c;把剧放在自己的系统内&#xff0c;让用户来充值&#xff0c;充值的钱全部都是我自己的&#xff…

【区块链+基础设施】蜀信链 | FISCO BCOS应用案例

蜀信链是在四川省经济和信息化厅指导下&#xff0c;在四川省区块链行业协会组织下&#xff0c;由全省区块链相关从业与应用机构 共同参与建设和运营的区域性区块链基础设施&#xff0c;通过多方协同&#xff0c;共同打造合作共赢的区块链产业生态。 蜀信链区块链服务生态秉承“…

ubuntu24.04LTS防火墙设置

Ubuntu24.04LTS开箱自带ufw&#xff0c;一定程度避免了开机下载ufw被攻击&#xff0c;excellent 转载aliyun教程 sudo ufw enbale可以启用并且开机自启(显示有效&#xff0c;未nmap实测) 教程3 转载自CSDN 完整格式如下&#xff1a; # 禁止IP连接端口 sudo ufw deny proto tc…

CSS filter(滤镜)属性,并实现页面置灰效果

目录 一、filter&#xff08;滤镜&#xff09;属性 二、准备工作 三、常用的filter属性值 1、blur(px) 2、brightness(%) 3、contrast(%) 4、grayscale(%) 5、opacity(%) 6、saturate(%) 7、sepia(%) 8、invert(%) 9、hue-rotate(deg) 10、drop-shadow(h-shadow v…

java生成excel,uniapp微信小程序接收excel并打开

java引包&#xff0c;引的是apache.poi <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency> 写一个测试类&#xff0c;把excel输出到指定路径 public s…

汽车电子工程师入门系列——AUTOSAR通信服务框架(下)

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

关于 lvds 屏幕的一些知识

网上的截图&#xff1a; lvds的 通道。 lvds 的协议 关于 sync 模式与 de 模式&#xff1a; ------------------------------------------------------------------------------------------------------------------ 芯片的数据手册的看法。 这个手册 &#xff0c;就指明了…

代码托管服务:GitHub、GitLab、Gitee

目录 引言GitHub&#xff1a;全球最大的代码托管平台概述功能特点适用场景 GitLab&#xff1a;一体化的开发平台概述功能特点适用场景 Gitee&#xff08;码云&#xff09;&#xff1a;中国本土化的代码托管服务概述功能特点适用场景 功能对比结论 引言 在现代软件开发中&#…

【路由交换技术】Cisco Packet Tracer基础入门教程(五)

这一期我们来学习端口聚合&#xff0c;这是针对交换机的技术 前言 不知道大家有没有注意到&#xff0c;我们之前的实验在交换机与交换机之间只用一条线连接&#xff0c;像这样 通过今天的学习&#xff0c;我们要用两条线来连接交换机&#xff0c;就像这样&#xff08;为了能…

Postman接口测试工具详解【保姆级教程】

大家好,我是CodeQi! 在我们日常的开发工作中,无论是前端还是后端,API 接口的测试都是必不可少的一环。 你有没有遇到过这样的情况:接口测试工具复杂难用,使用起来让人抓狂;或者手动构造请求效率低下,容易出错? 别担心,我今天要介绍的 Postman 工具,将会彻底改变你…

List接口, ArrayList Vector LinkedList

Collection接口的子接口 子类Vector&#xff0c;ArrayList&#xff0c;LinkedList 1.元素的添加顺序和取出顺序一致&#xff0c;且可重复 2.每个元素都有其对应的顺序索引 方法 在index 1 的位置插入一个对象&#xff0c;list.add(1,list2)获取指定index位置的元素&#…

概率论与数理统计_下_科学出版社

contents 前言第5章 大数定律与中心极限定理独立同分布中心极限定理 第6章 数理统计的基本概念6.1 总体与样本6.2 经验分布与频率直方图6.3 统计量6.4 正态总体抽样分布定理6.4.1 卡方分布、t 分布、F 分布6.4.2 正态总体抽样分布基本定理 第7章 参数估计7.1 点估计7.1.1 矩估计…

C++中的类型转换操作符:static_cast reinterpret_cast const_cast dynamic_cast

目录​​​​​​​ C语言中的类型转换 C中的类型转换 C中的类型转换操作符 static_cast reinterpret_cast const_cast volatile关键字 赋值兼容 dynamic_cast C语言中的类型转换 基本概念&#xff1a;赋值运算符左右两侧类型不同&#xff0c;或形参与实参类型不匹配…

抖音微短剧小程序源码搭建:实现巨量广告数据高效回传

在数字化营销日益盛行的今天&#xff0c;抖音微短剧小程序已成为品牌与观众互动的新渠道。这些短小精悍的剧目不仅能迅速抓住用户的注意力&#xff0c;还能有效提升品牌的知名度和用户黏性。然而&#xff0c;想要充分利用这一营销工具&#xff0c;关键在于如何高效地追踪广告数…

【PyQt】20-QTimer(动态显示时间、定时关闭)

QTimer 前言一、QTimer介绍二、动态时间展示2.1 代码2.2 运行结果 三、定时关闭3.1 介绍他的两种用法1、使用函数或Lambda表达式2、带有定时器类型&#xff08;高级&#xff09; 3.2 代码3.3 运行结果 总结 前言 好久没学习了。 一、QTimer介绍 pyqt里面的多线程可以有两种实…

【python数据处理】— “2020-01-01 05:20:15“日期格式数据

文章目录 一、数据说明及目标二、实现方式1.提取date2.提取hour3.提取weekday4.提取month 一、数据说明及目标 数据说明 数据表有一列名为"datetime"表示时间数据&#xff0c;该列的数据格式是"2020-01-01 05:20:15"。 import pandas as pd datapd.read_e…

基于SpringBoot的超市进销存系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架 工具&#xff1a;MyEclipse、Tomcat 系统展示 首页 首页界面图 个人中心 个人中心…