什么是 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. nginx与location规则

    ========================================================================= 2018年3月28日 记录: location = ...

  2. 一键自动化博客发布工具,用过的人都说好(cnblogs篇)

    cnblogs和其他的博客平台相比会比较复杂,需要设置的项目也比较多一些,弄懂了cnblogs的实现方式,那么你应该对selenium的整个框架使用已经烂熟于心了. 除了正常的标题,内容,摘要之外,c ...

  3. 🔥httpsok-v1.11.0支持CDN证书自动部署

    httpsok-v1.11.0支持CDN证书自动部署 介绍 httpsok 是一个便捷的 HTTPS 证书自动续签工具,专为 Nginx .OpenResty 服务器设计.已服务众多中小企业,稳定.安 ...

  4. PageOffice6 版本常用事件

    1.AfterDocumentOpened 事件 打开文件后自动触发的事件是 AfterDocumentOpened 事件,它通常用于实现文件打开后自动执行某些业务逻辑,比如将默认控件全屏.禁止保存. ...

  5. mongodb的备份与恢复详解

    简单 Mongodb导出与导入 1: 导入/导出可以操作的是本地的mongodb服务器,也可以是远程的.所以,都有如下通用选项:-h host 主机--port port 端口-u username ...

  6. 【漏洞复现】用友NC-Cloud PMCloudDriveProjectStateServlet接口存在JNDI注入漏洞

    阅读须知 花果山的技术文章仅供参考,此文所提供的信息只为网络安全人员对自己所负责的网站.服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作.利用此 ...

  7. text/event-stream协议

    客户端接收 text/event-stream html <!DOCTYPE html> <html> <head> <meta charset=" ...

  8. linux file命令查看文件类型

    在linux系统中,linux是不根据后缀名识别文件类型的,所以使用file命令查看文件的类型. [root@node5 ~]# file /etc/shadow /etc/shadow: ASCII ...

  9. 带你阅读Naive Ui Admin后台管理源码,并手撸JS版本

    Naive Ui Admin 是一个基于 Vue3.0.Vite. Naive UI.TypeScript 的中后台解决方案,它使用了最新的前端技术栈,并提炼了典型的业务模型,页面,包括二次封装组件. ...

  10. 大数据之Hadoop集群的HDFS压力测试

    测试HDFS写性能 原文:sw-code 1)写测试的原理 2)测试内容:向HDFS集群写10个128MB的文件(3个机器每个4核,2 * 4 = 8 < 10 < 3 * 4 =12) ...