AOP +FreeSql 跨方法异步事务
AOP +FreeSql 跨方法异步事务
- Autofac.Extensions.DependencyInjection
- Autofac.Extras.DynamicProxy
- Castle.Core.AsyncInterceptor(异步方法AOP拦截)
源码
csproj
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="5.0.0" />
<PackageReference Include="Castle.Core.AsyncInterceptor" Version="1.7.0" />
使用Autofac实现特性标签,事务处理
创建一个标识事务的特性标签
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class TransactionalAttribute : Attribute
{
/// <summary>
/// 事务传播方式
/// </summary>
public Propagation Propagation { get; set; } = Propagation.Required;
/// <summary>
/// 事务隔离级别
/// </summary>
public IsolationLevel? IsolationLevel { get; set; }
}
Autofac
Program.CS 替换默认的DI CreateHostBuilder方法
Host.CreateDefaultBuilder(args).UseServiceProviderFactory(new AutofacServiceProviderFactory())
Startup.cs配置服务
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new AutofacModule());
}
这里给BlogService方法注入UnitOfWorkInterceptor拦截处理。直接注入类。
public class AutofacModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<UnitOfWorkInterceptor>();
builder.RegisterType<UnitOfWorkAsyncInterceptor>();
builder.RegisterType<BlogService>()
.InterceptedBy(typeof(UnitOfWorkInterceptor))
.EnableClassInterceptors();
}
- 当然我们也能使用autofac批量注入以Service后缀的接口。该方法在lin-cms-dotnetcore项目中有使用https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Web/Startup/Configuration/ServiceModule.cs
List<Type> interceptorServiceTypes = new List<Type>()
{
typeof(UnitOfWorkInterceptor)
};
//service所在dll
Assembly servicesDllFile = Assembly.Load("LinCms.Application");
builder.RegisterAssemblyTypes(servicesDllFile)
.Where(a => a.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope()
.PropertiesAutowired()// 属性注入
.InterceptedBy(interceptorServiceTypes.ToArray())
.EnableInterfaceInterceptors();
AOP
public class UnitOfWorkInterceptor : IInterceptor
{
private readonly UnitOfWorkAsyncInterceptor asyncInterceptor;
public UnitOfWorkInterceptor(UnitOfWorkAsyncInterceptor interceptor)
{
asyncInterceptor = interceptor;
}
public void Intercept(IInvocation invocation)
{
asyncInterceptor.ToInterceptor().Intercept(invocation);
}
}
public class UnitOfWorkAsyncInterceptor : IAsyncInterceptor
{
private readonly UnitOfWorkManager _unitOfWorkManager;
private readonly ILogger<UnitOfWorkAsyncInterceptor> _logger;
IUnitOfWork _unitOfWork;
public UnitOfWorkAsyncInterceptor(UnitOfWorkManager unitOfWorkManager, ILogger<UnitOfWorkAsyncInterceptor> logger)
{
_unitOfWorkManager = unitOfWorkManager;
_logger = logger;
}
private bool TryBegin(IInvocation invocation)
{
//_unitOfWork = _unitOfWorkManager.Begin(Propagation.Requierd);
//return true;
var method = invocation.MethodInvocationTarget ?? invocation.Method;
var attribute = method.GetCustomAttributes(typeof(TransactionalAttribute), false).FirstOrDefault();
if (attribute is TransactionalAttribute transaction)
{
_unitOfWork = _unitOfWorkManager.Begin(transaction.Propagation, transaction.IsolationLevel);
return true;
}
return false;
}
/// <summary>
/// 拦截同步执行的方法
/// </summary>
/// <param name="invocation"></param>
public void InterceptSynchronous(IInvocation invocation)
{
if (TryBegin(invocation))
{
int? hashCode = _unitOfWork.GetHashCode();
try
{
invocation.Proceed();
_logger.LogInformation($"----- 拦截同步执行的方法-事务 {hashCode} 提交前----- ");
_unitOfWork.Commit();
_logger.LogInformation($"----- 拦截同步执行的方法-事务 {hashCode} 提交成功----- ");
}
catch
{
_logger.LogError($"----- 拦截同步执行的方法-事务 {hashCode} 提交失败----- ");
_unitOfWork.Rollback();
throw;
}
finally
{
_unitOfWork.Dispose();
}
}
else
{
invocation.Proceed();
}
}
/// <summary>
/// 拦截返回结果为Task的方法
/// </summary>
/// <param name="invocation"></param>
public void InterceptAsynchronous(IInvocation invocation)
{
if (TryBegin(invocation))
{
invocation.ReturnValue = InternalInterceptAsynchronous(invocation);
}
else
{
invocation.Proceed();
}
}
private async Task InternalInterceptAsynchronous(IInvocation invocation)
{
string methodName =
$"{invocation.MethodInvocationTarget.DeclaringType?.FullName}.{invocation.Method.Name}()";
int? hashCode = _unitOfWork.GetHashCode();
using (_logger.BeginScope("_unitOfWork:{hashCode}", hashCode))
{
_logger.LogInformation($"----- async Task 开始事务{hashCode} {methodName}----- ");
invocation.Proceed();
try
{
await (Task)invocation.ReturnValue;
_unitOfWork.Commit();
_logger.LogInformation($"----- async Task 事务 {hashCode} Commit----- ");
}
catch (System.Exception)
{
_unitOfWork.Rollback();
_logger.LogError($"----- async Task 事务 {hashCode} Rollback----- ");
throw;
}
finally
{
_unitOfWork.Dispose();
}
}
}
/// <summary>
/// 拦截返回结果为Task<TResult>的方法
/// </summary>
/// <param name="invocation"></param>
/// <typeparam name="TResult"></typeparam>
public void InterceptAsynchronous<TResult>(IInvocation invocation)
{
invocation.ReturnValue = InternalInterceptAsynchronous<TResult>(invocation);
}
private async Task<TResult> InternalInterceptAsynchronous<TResult>(IInvocation invocation)
{
TResult result;
if (TryBegin(invocation))
{
string methodName = $"{invocation.MethodInvocationTarget.DeclaringType?.FullName}.{invocation.Method.Name}()";
int hashCode = _unitOfWork.GetHashCode();
_logger.LogInformation($"----- async Task<TResult> 开始事务{hashCode} {methodName}----- ");
try
{
invocation.Proceed();
result = await (Task<TResult>)invocation.ReturnValue;
_unitOfWork.Commit();
_logger.LogInformation($"----- async Task<TResult> Commit事务{hashCode}----- ");
}
catch (System.Exception)
{
_unitOfWork.Rollback();
_logger.LogError($"----- async Task<TResult> Rollback事务{hashCode}----- ");
throw;
}
finally
{
_unitOfWork.Dispose();
}
}
else
{
invocation.Proceed();
result = await (Task<TResult>)invocation.ReturnValue;
}
return result;
}
}
没有接口,必须使用virtual虚方法。
public class BlogService
{
/// <summary>
/// 当出现异常时,不会插入数据
/// </summary>
/// <param name="createBlogDto"></param>
[Transactional]
public virtual void CreateBlogTransactional(CreateBlogDto createBlogDto)
{
Blog blog = _mapper.Map<Blog>(createBlogDto);
blog.CreateTime = DateTime.Now;
_blogRepository.Insert(blog);
List<Tag> tags = new List<Tag>();
createBlogDto.Tags.ForEach(r =>
{
tags.Add(new Tag { TagName = r });
});
if (createBlogDto.Title == "abc")
{
throw new Exception("test exception");
}
_tagRepository.Insert(tags);
}
}
AOP +FreeSql 跨方法异步事务的更多相关文章
- Asp.netCore 3.1控制器属性注入and异步事务Aop by AutoFac
Aspect Oriented Programming(AOP)是较为热门的一个话题.AOP,国内我们都习惯称之为:面向切面编程 下面直接code 干货展示:(一般人我还不告诉,嘻嘻) 1:导入相关的 ...
- 使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。
使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务.
- Spring.net 间接调用被AOP拦截的方法失效(无法进入aop的拦截方法)
.下面的tx要定义 <objects xmlns="http://www.springframework.net" xmlns:db="http://www.spr ...
- spring声明式事务 同一类内方法调用事务失效
只要避开Spring目前的AOP实现上的限制,要么都声明要事务,要么分开成两个类,要么直接在方法里使用编程式事务 [问题] Spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了Spring ...
- (H5)FormData+AJAX+SpringMVC跨域异步上传文件
最近都没时间整理资料了,一入职就要弄懂业务,整天被业务弄得血崩. 总结下今天弄了一个早上的跨域异步上传文件.主要用到技术有HTML5的FormData,AJAX,Spring MVC. 首先看下上传页 ...
- 【事务】<查询不到同一调用方法其它事务提交的更新>解决方案
最近遇到一个很棘手的问题,至今也解释不清楚原因,不过已经找到了解决方案. 先来看看Propagation属性的值含义,@Transactional中Propagation属性有7个选项可供选择: Pr ...
- spring声明式事务 同一类内方法调用事务失效(转)
原文 https://blog.csdn.net/jiesa/article/details/53438342 [问题] Spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了Spring ...
- Spring开启方法异步执行
@EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(Async ...
- Jquery~跨域异步上传文件
先说明白 这个跨域异步上传功能我们借助了Jquery.form插件,它在异步表单方面很有成效,而跨域我们会在HTTP响应头上添加access-control-allow-method,当然这个头标记只 ...
随机推荐
- 2019牛客暑期多校训练营(第二场) H-Second Large Rectangle(单调栈)
题意:给出由01组成的矩阵,求求全是1的次大子矩阵. 思路: 单调栈 全是1的最大子矩阵的变形,不能直接把所有的面积存起来然后排序取第二大的,因为次大子矩阵可能在最大子矩阵里面,比如: 1 0 0 1 ...
- 2019 Multi-University Training Contest 4.Divide the Stones(贪心)
题意:给你n和k (k|n) 有n个数 第i个数权值为i 要你求权值相同且分成k组 且每组的个数为n/k 思路:恶心构造题,首先对于总权值不能分为k份的 显然不能分成 然后 我们把n/k 分奇偶 我们 ...
- hdu 4352 XHXJ's LIS(数位dp+状压)
Problem Description #define xhxj (Xin Hang senior sister(学姐)) If you do not know xhxj, then carefull ...
- 2019icpc徐州站 Cat 计蒜客 - 42540 && The Answer to the Ultimate Question of Life, The Universe, and Everything. 计蒜客 - 42545
VJ链接:https://vjudge.net/contest/412095#problem/A Cat 计蒜客 - 42540 题意: 给你一个区间[L,R],给你现在拥有的钱S.你需要从[L,R] ...
- HDU - 3613 Best Reward(manacher或拓展kmp)
传送门:HDU - 3613 题意:给出26个字母的价值,然后给你一个字符串,把它分成两个字符串,字符串是回文串才算价值,求价值最大是多少. 题解:这个题可以用马拉车,也可以用拓展kmp. ①Mana ...
- Codeforces Round #529 (Div. 3) C. Powers Of Two (二进制)
题意:给你一个数\(n\),问是否能有\(k\)个\(2\)次方的数构成,若满足,输出一种合法的情况. 题解:从高到低枚举二进制的每一位,求出\(n\)的二进制的\(1\)的位置放进优先队列中,因为\ ...
- NLNet-Theme for cnblogs
这篇文档仅作为markdown在cnblogs中的渲染效果展示.第一部分NLNet' Samples为自定义内容的效果展示.NOTE 第二.三部分的Markdown Reference(From Ty ...
- CF1475-D. Cleaning the Phone
CF1475-D. Cleaning the Phone 题意: 手机上有很多应用非常占用内存,你要清理内存.对于每个应用\(i\)有以下描述:应用\(i\)占用了\(a_i\)的空间,它的方便度为\ ...
- ArcMobile的CoordinateCollection在逆时针添加点时自动调整节点顺序的问题
为了使用ArcMobile实现量测功能,LZ自定义了一个MapGraphicLayer用于绘图,代码如下: using System.Drawing; using ESRI.ArcGIS.Mobile ...
- Linux 驱动框架---linux 驱动
总述 Linux 系统下的驱动最后都是以如下这个结构体呈现在系统中的,注意其中的dev_pm_ops是内核新增的内容来准备替换platform_driver中的电源管理相关的内容.这里内容是先进行总体 ...