利用AOP实现SqlSugar自动事务
作者:若若若邪 发布时间:2021-11-24 11:56:42
本文实例为大家分享了如何利用AOP实现SqlSugar自动事务,供大家参考,具体内容如下
先看一下效果,带接口层的三层架构:
BL层:
public class StudentBL : IStudentService
{
private ILogger mLogger;
private readonly IStudentDA mStudentDa;
private readonly IValueService mValueService;
public StudentService(IStudentDA studentDa,IValueService valueService)
{
mLogger = LogManager.GetCurrentClassLogger();
mStudentDa = studentDa;
mValueService = valueService;
}
[TransactionCallHandler]
public IList<Student> GetStudentList(Hashtable paramsHash)
{
var list = mStudentDa.GetStudents(paramsHash);
var value = mValueService.FindAll();
return list;
}
}
假设GetStudentList方法里的mStudentDa.GetStudents和mValueService.FindAll不是查询操作,而是更新操作,当一个失败另一个需要回滚,就需要在同一个事务里,当一个出现异常就要回滚事务。
特性TransactionCallHandler就表明当前方法需要开启事务,并且当出现异常的时候回滚事务,方法执行完后提交事务。
DA层:
public class StudentDA : IStudentDA
{
private SqlSugarClient db;
public StudentDA()
{
db = SugarManager.GetInstance().SqlSugarClient;
}
public IList<Student> GetStudents(Hashtable paramsHash)
{
return db.Queryable<Student>().AS("T_Student").With(SqlWith.NoLock).ToList();
}
}
对SqlSugar做一下包装
public class SugarManager
{
private static ConcurrentDictionary<string,SqlClient> _cache =
new ConcurrentDictionary<string, SqlClient>();
private static ThreadLocal<string> _threadLocal;
private static readonly string _connStr = @"Data Source=localhost;port=3306;Initial Catalog=thy;user id=root;password=xxxxxx;Charset=utf8";
static SugarManager()
{
_threadLocal = new ThreadLocal<string>();
}
private static SqlSugarClient CreatInstance()
{
SqlSugarClient client = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = _connStr, //必填
DbType = DbType.MySql, //必填
IsAutoCloseConnection = true, //默认false
InitKeyType = InitKeyType.SystemTable
});
var key=Guid.NewGuid().ToString().Replace("-", "");
if (!_cache.ContainsKey(key))
{
_cache.TryAdd(key,new SqlClient(client));
_threadLocal.Value = key;
return client;
}
throw new Exception("创建SqlSugarClient失败");
}
public static SqlClient GetInstance()
{
var id= _threadLocal.Value;
if (string.IsNullOrEmpty(id)||!_cache.ContainsKey(id))
return new SqlClient(CreatInstance());
return _cache[id];
}
public static void Release()
{
try
{
var id = GetId();
if (!_cache.ContainsKey(id))
return;
Remove(id);
}
catch (Exception e)
{
throw e;
}
}
private static bool Remove(string id)
{
if (!_cache.ContainsKey(id)) return false;
SqlClient client;
int index = 0;
bool result = false;
while (!(result = _cache.TryRemove(id, out client)))
{
index++;
Thread.Sleep(20);
if (index > 3) break;
}
return result;
}
private static string GetId()
{
var id = _threadLocal.Value;
if (string.IsNullOrEmpty(id))
{
throw new Exception("内部错误: SqlSugarClient已丢失.");
}
return id;
}
public static void BeginTran()
{
var instance=GetInstance();
//开启事务
if (!instance.IsBeginTran)
{
instance.SqlSugarClient.Ado.BeginTran();
instance.IsBeginTran = true;
}
}
public static void CommitTran()
{
var id = GetId();
if (!_cache.ContainsKey(id))
throw new Exception("内部错误: SqlSugarClient已丢失.");
if (_cache[id].TranCount == 0)
{
_cache[id].SqlSugarClient.Ado.CommitTran();
_cache[id].IsBeginTran = false;
}
}
public static void RollbackTran()
{
var id = GetId();
if (!_cache.ContainsKey(id))
throw new Exception("内部错误: SqlSugarClient已丢失.");
_cache[id].SqlSugarClient.Ado.RollbackTran();
_cache[id].IsBeginTran = false;
_cache[id].TranCount = 0;
}
public static void TranCountAddOne()
{
var id = GetId();
if (!_cache.ContainsKey(id))
throw new Exception("内部错误: SqlSugarClient已丢失.");
_cache[id].TranCount++;
}
public static void TranCountMunisOne()
{
var id = GetId();
if (!_cache.ContainsKey(id))
throw new Exception("内部错误: SqlSugarClient已丢失.");
_cache[id].TranCount--;
}
}
_cache保存SqlSugar实例,_threadLocal确保同一线程下取出的是同一个SqlSugar实例。
不知道SqlSugar判断当前实例是否已经开启事务,所以又将SqlSugar包了一层。
public class SqlClient
{
public SqlSugarClient SqlSugarClient;
public bool IsBeginTran = false;
public int TranCount = 0;
public SqlClient(SqlSugarClient sqlSugarClient)
{
this.SqlSugarClient = sqlSugarClient;
}
}
IsBeginTran标识当前SqlSugar实例是否已经开启事务,TranCount是一个避免事务嵌套的计数器。
一开始的例子
[TransactionCallHandler]
public IList<Student> GetStudentList(Hashtable paramsHash)
{
var list = mStudentDa.GetStudents(paramsHash);
var value = mValueService.FindAll();
return list;
}
TransactionCallHandler表明该方法要开启事务,但是如果mValueService.FindAll也标识了TransactionCallHandler,又要开启一次事务?所以用TranCount做一个计数。
使用Castle.DynamicProxy
要实现标识了TransactionCallHandler的方法实现自动事务,使用Castle.DynamicProxy实现BL类的代理
Castle.DynamicProxy一般操作
public class MyClass : IMyClass
{
public void MyMethod()
{
Console.WriteLine("My Mehod");
}
}
public class TestIntercept : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("before");
invocation.Proceed();
Console.WriteLine("after");
}
}
var proxyGenerate = new ProxyGenerator();
TestIntercept t=new TestIntercept();
var pg = proxyGenerate.CreateClassProxy<MyClass>(t);
pg.MyMethod();
//输出是
//before
//My Mehod
//after
before就是要开启事务的地方,after就是提交事务的地方
最后实现
public class TransactionInterceptor : IInterceptor
{
private readonly ILogger logger;
public TransactionInterceptor()
{
logger = LogManager.GetCurrentClassLogger();
}
public void Intercept(IInvocation invocation)
{
MethodInfo methodInfo = invocation.MethodInvocationTarget;
if (methodInfo == null)
{
methodInfo = invocation.Method;
}
TransactionCallHandlerAttribute transaction =
methodInfo.GetCustomAttributes<TransactionCallHandlerAttribute>(true).FirstOrDefault();
if (transaction != null)
{
SugarManager.BeginTran();
try
{
SugarManager.TranCountAddOne();
invocation.Proceed();
SugarManager.TranCountMunisOne();
SugarManager.CommitTran();
}
catch (Exception e)
{
SugarManager.RollbackTran();
logger.Error(e);
throw e;
}
}
else
{
invocation.Proceed();
}
}
}
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class TransactionCallHandlerAttribute : Attribute
{
public TransactionCallHandlerAttribute()
{
}
}
Autofac与Castle.DynamicProxy结合使用
创建代理的时候一个BL类就要一次操作
proxyGenerate.CreateClassProxy<MyClass>(t);
而且项目里BL类的实例化是交给IOC容器控制的,我用的是Autofac。当然Autofac和Castle.DynamicProxy是可以结合使用的
using System.Reflection;
using Autofac;
using Autofac.Extras.DynamicProxy;
using Module = Autofac.Module;
public class BusinessModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var business = Assembly.Load("FTY.Business");
builder.RegisterAssemblyTypes(business)
.AsImplementedInterfaces().InterceptedBy(typeof(TransactionInterceptor)).EnableInterfaceInterceptors();
builder.RegisterType<TransactionInterceptor>();
}
}
来源:http://www.cnblogs.com/jaycewu/archive/2017/10/25/7733114.html
猜你喜欢
- openFeign服务间调用保持请求头信息处理1、注意特殊情况,在定时任务或者内部之间调用,没有request的时候,不要处理直接返回。2、
- 1,实现方法一:通过给当前界面布局文件的父layout设置点击事件(相当于给整个Activity设置点击事件),在事件里进行键盘隐藏<
- 前言本文将提供一个redis的工具类,可以用在Spring boot以及Spring Cloud项目中,本工具类主要整合了将Redis作为N
- 在有些开发场景,需要对 List 对象列表进行过滤处理,并将有用的数据存放到Map中。例如:告警对象,包含告警uuid(alarmUuid)
- 本文实例讲述了Android编程之ICS式下拉菜单PopupWindow实现方法。分享给大家供大家参考,具体如下:运行效果截图如下:右边这个
- private void btnSave_Click(object sender, RoutedEventArgs e)
- 一、前言我们先来看下面一个例子:using System;using System.Threading;namespace ThreadSy
- SpringBoot配置文件优先级前面SpringBoot基础有提到,关于SpringBoot配置文件可以是properties或者是yam
- 解析:CLR支持两种类型:值类型和引用类型。用Jeffrey Richter(《CLR via C#》作者)的话来说,“不理解引用类型和值类
- 一、前言Redis是一个NoSQL(非关系型数据库)数据库之一,key-value存储系统或者说是一个缓存键值对数据库,具有如下特性:基于内
- 需求最近小编的项目中出现了很多feign 调用出现 Read Time out 的异常,但因为没有集成链路追踪的第三方框架,查不到原因。所以
- 本文实例讲述了C#实现缩放和剪裁图片的方法。分享给大家供大家参考,具体如下:using System;using System.Collec
- 1.概述其实最简单的办法就是使用原生sql,如 session.createSQLQuery("sql"),或者使用jd
- 1.System.currentTimeMills():得到当前时间距离时间原点的毫秒数,返回值是Long类型的整数。代码演示:public
- 在 Java 语言中,运算符有算数运算符、关系运算符、逻辑运算符、赋值运算符、字符串连接运算符、条件运算符。算数运算符算数运算符是我们最常用
- 当我们要创建一个Tcp/Ip Server connection ,我们需要一个范围在1000到65535之间的端口 。但是本机一个端口只能
- 1. 背景在业务处理完之后,需要调用其他系统的接口,将相应的处理结果通知给对方,若是同步请求,假如调用的系统出现异常或是宕机等事件,会导致自
- 我们很多时候会碰到这样的问题,使用多线程刷一个表的数据时需要多个线程不能重复提取数据,那么这个时候就需要使用到线程的排他锁了。在c#里面其实
- java 同步、异步、阻塞和非阻塞分析概要:正常情况下,我们的程序以同步非阻塞的方式在运行。但是我们的程序总会出现一些耗时操作,比如复杂的计
- 1.前置准备默认服务器上的hadoop服务已经启动本地如果是windows环境,需要本地配置下hadoop的环境变量本地配置hadoop的环