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(); }
    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 跨方法异步事务的更多相关文章

  1. Asp.netCore 3.1控制器属性注入and异步事务Aop by AutoFac

    Aspect Oriented Programming(AOP)是较为热门的一个话题.AOP,国内我们都习惯称之为:面向切面编程 下面直接code 干货展示:(一般人我还不告诉,嘻嘻) 1:导入相关的 ...

  2. 使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。

    使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务.

  3. Spring.net 间接调用被AOP拦截的方法失效(无法进入aop的拦截方法)

    .下面的tx要定义 <objects xmlns="http://www.springframework.net" xmlns:db="http://www.spr ...

  4. spring声明式事务 同一类内方法调用事务失效

    只要避开Spring目前的AOP实现上的限制,要么都声明要事务,要么分开成两个类,要么直接在方法里使用编程式事务 [问题] Spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了Spring ...

  5. (H5)FormData+AJAX+SpringMVC跨域异步上传文件

    最近都没时间整理资料了,一入职就要弄懂业务,整天被业务弄得血崩. 总结下今天弄了一个早上的跨域异步上传文件.主要用到技术有HTML5的FormData,AJAX,Spring MVC. 首先看下上传页 ...

  6. 【事务】<查询不到同一调用方法其它事务提交的更新>解决方案

    最近遇到一个很棘手的问题,至今也解释不清楚原因,不过已经找到了解决方案. 先来看看Propagation属性的值含义,@Transactional中Propagation属性有7个选项可供选择: Pr ...

  7. spring声明式事务 同一类内方法调用事务失效(转)

    原文 https://blog.csdn.net/jiesa/article/details/53438342 [问题] Spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了Spring ...

  8. Spring开启方法异步执行

    @EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(Async ...

  9. Jquery~跨域异步上传文件

    先说明白 这个跨域异步上传功能我们借助了Jquery.form插件,它在异步表单方面很有成效,而跨域我们会在HTTP响应头上添加access-control-allow-method,当然这个头标记只 ...

随机推荐

  1. 2019牛客暑期多校训练营(第五场)C - generator 2 (BSGS)

    题目链接 题意: 给定\(n,x_0,a,b,p\),有递推式\(x_i = (a \cdot x_{i-1} +b)\%p\). 有\(Q\)个询问,每次询问给定一个\(v\),问是否存在一个最小的 ...

  2. Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round) C. Remove Adjacent(字符串,贪心,枚举)

    题意: 给你一个由小写字母组成的字符串,若串中两个相邻元素字典序中也相邻,移除较大字母,问最多能移除多少个字母. 思路: 从大到小依次枚举. Tips: 注意下标的处理. 以小消大: #include ...

  3. AtCoder AIsing Programming Contest 2020 D - Anything Goes to Zero (二进制,模拟)

    题意:给你一个长度为\(n\)的\(01\)串,从高位到低位遍历,对该位取反,用得到的十进制数\(mod\)所有位上\(1\)的个数,不断循环直到为\(0\),输出每次遍历时循环的次数. 题解:根据题 ...

  4. 浅谈Webpack模块打包工具三

    Source Map 生产代码与开发代码完全不同,如果需要调试应用的话会非常的麻烦,错误信息无法定位,Soutce Map就会逆向得到源代码, 须在打包之后的代码文件的末尾位置例如添加//# sour ...

  5. CF1475-D. Cleaning the Phone

    CF1475-D. Cleaning the Phone 题意: 手机上有很多应用非常占用内存,你要清理内存.对于每个应用\(i\)有以下描述:应用\(i\)占用了\(a_i\)的空间,它的方便度为\ ...

  6. Python:装饰器是如何调用的

    应用举例:(1)装饰器 # 装饰器的调用: # 一旦用上装饰器会: # 第一步:调outer函数 # 第二步:被装饰的函数play_game会被当作参数fn给outer # 第三步:最后调用play_ ...

  7. LINUX - openssl学习

    参考链接: https://www.cnblogs.com/AloneSword/p/4656492.html

  8. js的变量,作用域,内存

    一,基本类型和引用类型的值基本类型的值是按值访问的,引用类型的值是保存在内存中的对象1,动态的属性 只有引用类型的值可以添加属性方法 不能给基本类型添加属性和方法2,复制变量值 复制基本类型的值,两个 ...

  9. 记一次 lampiao渗透(Drupal+脏牛提权)

    vulnhub|渗透测试lampiao 题记 最近在打靶机,发现了一个挺有意思的靶机,这里想跟大家分享一下. 环境准备 vulnhub最近出的一台靶机 靶机(https://www.vulnhub.c ...

  10. Vue computed props pass params

    Vue computed props pass params vue 计算属性传参数 // 计算 spreaderAlias spreaderAlias () { console.log('this. ...