最近想给我的框架加一种功能,就是比如给一个方法加一个事务的特性Attribute,那这个方法就会启用事务处理。给一个方法加一个缓存特性,那这个方法就会进行缓存。

这个也是网上说的面向切面编程AOP。

AOP的概念也很好理解,跟中间件差不多,说白了,就是我可以任意地在方法的前面或后面添加代码,这很适合用于缓存、日志等处理。

在net core2.2时,我当时就尝试过用autofac实现aop,但这次我不想用autofac,我用了一个更轻量级的框架,AspectCore。

用起来非常非常的简单,但一开始还是走了一点弯路,主要是网上都是net core3以下的教程,3以下的使用方法跟之前有一些不同。

先安装NuGet包,包名:AspectCore.Extensions.DependencyInjection

然后在Program.cs类中增加一行代码,这是net core 3的不同之处,这句添加的代码,意思就是用AspectCore的IOC容器替换内置的。因为AOP需要依靠IOC实现,所以必须得替换掉内置的IOC。

public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
} public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
var dependAssemblyKey = BuildDependAssemblyKey();
if (!string.IsNullOrEmpty(dependAssemblyKey))
{
webBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, dependAssemblyKey);
}
})
//用AspectCore替换默认的IOC容器
.UseServiceProviderFactory(new DynamicProxyServiceProviderFactory());
}

然后在Startup.cs类中的ConfigureServices中添加代码。(其实这个加不加都可以,如果需要配置就加,例如全局的拦截器、只拦截哪些匹配的服务,因为我只用特性进行拦截,所以我就什么也没配置)

services.ConfigureDynamicProxy(o=> {
//添加AOP的配置
});

这样AOP就配置好了,是不是很简单。

当然使用方面也需要注意一下,可以在接口、接口的方法、类,类的virtual方法上进行拦截。还有如果你想拦截控制器的action的话,那需要在ConfigureService里AddControllerAsServices

services.AddControllers()
//把控制器当成服务
.AddControllersAsServices()

下面我列出我的事务拦截器代码

public class TransactionInterceptorAttribute : AbstractInterceptorAttribute
{
public async override Task Invoke(AspectContext context, AspectDelegate next)
{
var dbContext = context.ServiceProvider.GetService<AppDbContext>();
//先判断是否已经启用了事务
if (dbContext.Database.CurrentTransaction == null)
{
await dbContext.Database.BeginTransactionAsync();
try
{
await next(context);
dbContext.Database.CommitTransaction();
}
catch (Exception ex)
{
dbContext.Database.RollbackTransaction();
throw ex;
}
}
else
{
await next(context);
}
}
}

然后我就可以这么优雅地使用事务了

我再列出我的缓存拦截器

public class CacheInterceptorAttribute : AbstractInterceptorAttribute
{
/// <summary>
/// 缓存秒数
/// </summary>
public int ExpireSeconds { get; set; } public async override Task Invoke(AspectContext context, AspectDelegate next)
{
//先判断方法是否有返回值,无就不进行缓存判断
var returnParams = context.GetReturnParameter();
if (returnParams.Type == typeof(void))
{
await next(context);
return;
} //获取方法参数名
string param = CommonHelper.ObjectToJsonString(context.Parameters);
//获取方法名称,也就是缓存key值
string key = "Methods:" + context.ImplementationMethod.DeclaringType.FullName + "." + context.ImplementationMethod.Name;
var cache = context.ServiceProvider.GetService<ICacheHelper>();
//如果缓存有值,那就直接返回缓存值
if (cache.HashExists(key, param))
{
var value = typeof(ICacheHelper).GetMethod(nameof(ICacheHelper.HashGet)).MakeGenericMethod(returnParams.Type).Invoke(cache, new[] { key, param });
context.ReturnValue = value;
return;
}
await next(context);
cache.HashSet(key, param, context.ReturnValue);
if (ExpireSeconds > )
{
cache.SetExpire(key, TimeSpan.FromSeconds(ExpireSeconds));
} }
}

我还弄了一个缓存删除拦截器,作用就是带有这个特性的方法执行后,会删除相关缓存值

为什么有这个设计呢,比如说我给一个方法 GetUserList 加了缓存,那我数据改变了怎么办,我想在User数据改变时,把这个缓存删除掉,那我就可以在SaveUser方法上加上我这个缓存删除拦截器,那这个方法执行后,就会把相关的缓存删除掉了

public class CacheDeleteInterceptorAttribute : AbstractInterceptorAttribute
{
private readonly Type[] _types;
private readonly string[] _methods; /// <summary>
/// 需传入相同数量的Types跟Methods,同样位置的Type跟Method会组合成一个缓存key,进行删除
/// </summary>
/// <param name="Types">传入要删除缓存的类</param>
/// <param name="Methods">传入要删除缓存的方法名称,必须与Types数组对应</param>
public CacheDeleteInterceptorAttribute(Type[] Types, string[] Methods)
{
if (Types.Length != Methods.Length)
{
throw new ApiFailException(ApiFailCode.OPERATION_FAIL, "Types必须跟Methods数量一致");
}
_types = Types;
_methods = Methods;
} public async override Task Invoke(AspectContext context, AspectDelegate next)
{
var cache = context.ServiceProvider.GetService<ICacheHelper>();
await next(context);
for (int i = ; i < _types.Length; i++)
{
var type = _types[i];
var method = _methods[i];
string key = "Methods:" + type.FullName + "." + method;
cache.Delete(key);
}
}
}

AOP的实现原理我也想象了一下:

要实现AOP,需要依靠IOC容器,因为它是我们类的管家,那能被拦截的类必须是IOC注入的,自己new出来的是不受拦截的。如果我想在A方法前面添加点代码,那我告诉IOC,把代码给它,那IOC在注入A方法所在类时,会继承它生成一个派生类,然后重写A方法,所以拦截方法必须得为virtual,然后A方法里写上我要添加的代码,再base.A()这样。

Asp.net Core 3.1基于AspectCore实现AOP,实现事务、缓存拦截器的更多相关文章

  1. ASP.NET Core 实战:基于 Dapper 扩展你的数据访问方法

    一.前言 在非静态页面的项目开发中,必定会涉及到对于数据库的访问,最开始呢,我们使用 Ado.Net,通过编写 SQL 帮助类帮我们实现对于数据库的快速访问,后来,ORM(Object Relatio ...

  2. [译]如何在ASP.NET Core中实现面向切面编程(AOP)

    原文地址:ASPECT ORIENTED PROGRAMMING USING PROXIES IN ASP.NET CORE 原文作者:ZANID HAYTAM 译文地址:如何在ASP.NET Cor ...

  3. 打造静态分析器(二)基于Asp.Net Core 3.0的AspectCore组件检测

    上一篇,我们打造了一个简单的分析器,但是我们实际使用分析器就是为了对项目做分析检测,增加一些非语法的自检的 比如Asp.Net Core 3.0的替换依赖注入检测 设计分析 我们创建一个默认的Asp. ...

  4. ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露

    一.前言 在涉及到后端项目的开发中,如何实现对于用户权限的管控是需要我们首先考虑的,在实际开发过程中,我们可能会运用一些已经成熟的解决方案帮助我们实现这一功能,而在 Grapefruit.VuCore ...

  5. ASP.NET Core ResponseCaching:基于 VaryByHeader 定制缓存 Key

    ASP.NET Core ResponseCaching 提供了缓存http响应内容的能力,通过它可以在本地内存中直接缓存http响应内容,这是速度最快的服务端缓存,省却了网络传输与生成响应内容的开销 ...

  6. 在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度

    在这篇文章中,我将介绍如何使用ASP.NET Core托管服务运行Quartz.NET作业.这样的好处是我们可以在应用程序启动和停止时很方便的来控制我们的Job的运行状态.接下来我将演示如何创建一个简 ...

  7. ASP.NET Core 6.0 基于模型验证的数据验证

    1 前言 在程序中,需要进行数据验证的场景经常存在,且数据验证是有必要的.前端进行数据验证,主要是为了减少服务器请求压力,和提高用户体验:后端进行数据验证,主要是为了保证数据的正确性,保证系统的健壮性 ...

  8. Asp.Net Core 轻松学-基于微服务的后台任务调度管理器

    前言     在 Asp.Net Core 中,我们常常使用 System.Threading.Timer 这个定时器去做一些需要长期在后台运行的任务,但是这个定时器在某些场合却不太灵光,而且常常无法 ...

  9. .NetCore学习笔记:三、基于AspectCore的AOP事务管理

    AOP(面向切面编程),通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑 ...

随机推荐

  1. 表达式计算开源组件(NCalc.NetCore)

    首先,这款组件是开源的,NCalc是.net中的一个数学表达式求值程序.NCalc可以解析任何表达式并计算结果,包括静态或动态参数和自定义函数. 官网地址:http://ncalc.codeplex. ...

  2. 2020 最新省市区 sql

    一个基于有赞的 area.js 生成的sql area.js 简单的写了一个js 生成了sql语句 sql文件 完整代码

  3. 几个常见CSS错误和解决办法

    1.IE6下,当float存在时,margin双倍的问题 解决方法:加display:inline; 例: #content {    float: left;     width: 500px;   ...

  4. Netty 源码解析(七): NioEventLoop 工作流程

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第七篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源 ...

  5. Netty 源码解析(四): Netty 的 ChannelPipeline

    今天是猿灯塔“365篇原创计划”第四篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...

  6. js实现json格式化,以及json校验工具的简单实现

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, ...

  7. ATM + 购物车项目

    ''' 存放配置文件 ''' import os #获取项目根目录 BASE_PATH=os.path.dirname(os.path.dirname(__file__)) #获取用户目录 USER_ ...

  8. LDAP注入介绍

    LDAP注入介绍 一. 前言 前些日子在看 OWASP TOP 10 时看到了对LDAP注入攻击的介绍,对此产生了兴趣,在网上经过一番搜索之后找到了构成本文主要来源的资料,整理出来分享给大家. 二. ...

  9. JVM 专题二十一:垃圾回收(五)垃圾回收器 (二)

    3. 回收器 3.1 Serial回收器:串行回收 3.1.1 概述 Serial收集器是最基本.历史最悠久的垃圾收集器了.JDK1.3之前回收新生代唯一的选择. Serial收集器作为Hotspot ...

  10. python 并发专题(五):离散事件仿真(事件循环生成器)

    出租车队运营仿真 创建几辆出租车,每辆车会拉几个乘客,然后回家.出租车首先驶离车库,四处徘徊,寻找乘客:拉到乘客后,行程开始:乘客下车后,继续四处徘徊. 程序解释 程序的输出示例: 创建 3 辆出租车 ...