什么是 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. VSCode:所选环境中没有可用的Pip安装程序

    VSCode:所选环境中没有可用的Pip安装程序 然后我尝试格式化我的代码,VSCode说没有安装autopep8,可以通过Pip安装 . 但是,当我尝试通过Pip安装时,它会说 There is n ...

  2. Go语言—值类型和引用类型

    一.值类型 定义 变量直接存储的值,内存通常在栈中分配: var i = 5 -> i-->5 应用 int.float.bool.string.数组.struct 二.引用类型 1. 定 ...

  3. threejs

  4. Atera 用户为最终用户提供对办公计算机的远程访问

    ​一言以蔽之:由 Splashtop 提供支持的 Atera 的客户远程访问功能允许使用 Atera 的 MSP 设置和管理其最终用户对办公计算机的远程访问. 新冠肺炎大流行已加速了全球远程工作的进程 ...

  5. 互联网软件的安装包界面设计-Inno setup

    https://blog.csdn.net/oceanlucy/article/details/50033773 "安装界面太丑了,不堪入目!" "这界面应该属于20年代 ...

  6. 7.22考试总结(NOIP模拟23)[联·赛·题]

    不拼尽全力去试一下,又怎么会知道啊 前言 又是被细节问题搞掉的一天. T1 的话,与正解相差无几,少打了两个 else 一个 ls 打成了 rs,然后就爆零了(本来还有 45pts 的),然后加了一个 ...

  7. java中SimpleDateFormat解析日期格式的问题

    在日常写代码的过程中,我们经常要处理各种格式的日期,常见的日期格式有:"20240601","2024-06-01","2024-6-1". ...

  8. vue3+vant 引入Dialog Toast都会失败报错not defined

    今天在封装vant组件的时候,刚好要用到toast提示信息的组件,索性就按照官网提供的引入方法进行正常的引入,嘿,好家伙,一顿操作下来后发现竟然报Toast未定义,这就纳闷了,明明步骤都是对的啊,所以 ...

  9. 使用eNSP配置灵活QinQ

    参考链接:https://blog.csdn.net/xu119718/article/details/55260519 在"使用eNSP配置端口QinQ"实验中是基于端口划分的用 ...

  10. 内存优化:Boxing

    dotMemory 如今,许多开发人员都熟悉性能分析的工作流程:在分析器下运行应用程序,测量方法的执行时间,识别占用时间较多的方法,并致力于优化它们.然而,这种情况并没有涵盖到一个重要的性能指标:应用 ...