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

测试

实现用户自动填充

上下户用户

    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 以及实现事务、用户填充功能的更多相关文章

  1. 技术的正宗与野路子 c#, AOP动态代理实现动态权限控制(一) 探索基于.NET下实现一句话木马之asmx篇 asp.net core 系列 9 环境(Development、Staging 、Production)

    黄衫女子的武功似乎与周芷若乃是一路,飘忽灵动,变幻无方,但举手抬足之间却是正而不邪,如说周芷若形似鬼魅,那黄衫女子便是态拟神仙. 这段描写出自<倚天屠龙记>第三十八回. “九阴神抓”本是& ...

  2. # ASP.NET Core依赖注入解读&使用Autofac替代实现

    标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...

  3. asp.net core系列 30 EF管理数据库架构--必备知识 迁移

    一.管理数据库架构概述 EF Core 提供两种主要方法来保持 EF Core 模型和数据库架构同步.一是以 EF Core 模型为基准,二是以数据库为基准. (1)如果希望以 EF Core 模型为 ...

  4. asp.net core系列 40 Web 应用MVC 介绍与详细示例

    一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...

  5. asp.net core系列 39 Web 应用Razor 介绍与详细示例

    一. Razor介绍 在使用ASP.NET Core Web开发时, ASP.NET Core MVC 提供了一个新特性Razor. 这样开发Web包括了MVC框架和Razor框架.对于Razor来说 ...

  6. asp.net core系列 36 WebAPI 搭建详细示例

    一.概述 HTTP不仅仅用于提供网页.HTTP也是构建公开服务和数据的API强大平台.HTTP简单灵活且无处不在.几乎任何你能想到的平台都有一个HTTP库,因此HTTP服务可以覆盖广泛的客户端,包括浏 ...

  7. asp.net core系列 31 EF管理数据库架构--必备知识 反向工程

    一.   反向工程 反向工程是基于数据库架构,生成的实体类和DbContext类代码的过程,对于Visual Studio开发,建议使用PMC.对于其他开发环境,请选择.NET Core CLI工具( ...

  8. asp.net core 系列 18 web服务器实现

    一. ASP.NET Core Module 在介绍ASP.NET Core Web实现之前,先来了解下ASP.NET Core Module.该模块是插入 IIS 管道的本机 IIS 模块(本机是指 ...

  9. asp.net core 系列 17 通用主机 IHostBuilder

    一.概述 ASP.NET Core 通用主机 (HostBuilder),该主机对于托管不处理 HTTP 请求的应用非常有用.通用主机的目标是将 HTTP 管道从 Web 主机 API 中分离出来,从 ...

  10. ASP.NET Core依赖注入解读&使用Autofac替代实现【转载】

    ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac实现和自定义实现扩展方法 3.1 安装Autof ...

随机推荐

  1. element Tree 树形控件

    文档地址 https://element.eleme.cn/#/zh-CN/component/tree 代码地址 https://gitee.com/wBekvam/vue-shop-admin/b ...

  2. AIRIOT物联网低代码平台如何配置Modbus RTU协议?

    MBRTU即MODBUS RTU的简称,MODBUS是OSI模型第7层上的应用层报文传输协议,它在连接至不同类型总线或网络的设备之间提供客户机/服务器通信.平台的MBRTU协议是建立在TCP协议之上的 ...

  3. linux文件权限管理:文件权限类型,文件权限影响,设定文件权限,取消文件权限

    目录 一.关于文件权限 二.查看文件权限 三.linux下常见文件类型 四.linux下常见的文件权限 五.权限对文件和目录的影响 六.文件的用户分类 七.更改文件的属主和属组 八.一个文件取消所有权 ...

  4. RocketMQ消息过滤机制源码详解

    #RocketMQ提供了2种消息过滤的方式: TAG 过滤 SQL92 过滤 SQL过滤默认是没有打开的,如果想要支持,必须在broker的配置文件中设置:enablePropertyFilter = ...

  5. Django——启动项目时报错mysqlclient

    报错内容如下: Watching for file changes with StatReloader Exception in thread django-main-thread: Tracebac ...

  6. Rainbond 5.5 发布,支持Istio和扩展第三方Service Mesh框架

    Rainbond 5.5 版本主要优化扩展性.服务治理模式可以扩展第三方 ServiceMesh 架构,兼容kubernetes 管理命令和第三方管理平台. 主要功能点解读: 1. 支持 Istio, ...

  7. CTFshow-Crypto(1-5)

    1密码学签到 自己倒序 在线网站倒序 文字倒序工具,在线文字倒序 (qqxiuzi.cn) python脚本 a = '}wohs.ftc{galf' print(a[::-1], end=" ...

  8. 7.18考试总结(NOIP模拟19)[u·v·w]

    我们不是狼,我们只是长着獠牙的羊...... 前言 我真 TM 爱死 \(\frac{1}{4}\) 了. 老实说,这套题是真恶心,第一题还有一点思路,到了后面是一点都搞不定了. 总的来说,主要原因是 ...

  9. 规则引擎easyRules中组合规则的使用

    这两天想要做支付路由,要根据支付信息将支付请求转发到不同的支付渠道,可能要用到规则引擎,于是研究了一下开源的规则引擎产品.规则引擎核心知识与开源产品对比选型 ,这篇文章对规格引擎和开源产品有详细的介绍 ...

  10. RHEL9破解root密码

    使用rhel8版本的破解方法破解rhel9版本root密码时候,出现问题 在内核参数重置页面输了rd.break后,要么ctrl+d 要么输密码 rhel9破解root密码,应该选择的内核模式为第二行 ...