利用AOP实现SqlSugar自动事务
本文实例为大家分享了如何利用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>(); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Rstudio支持可视化的Markdown编辑了?
- (八)python3 只需3小时带你轻松入门——List 与 dict 的常用操作
- (九)python3 只需3小时带你轻松入门——函数自定义
- (十)python3 只需3小时带你轻松入门——模块与包
- (十一)python3 只需3小时带你轻松入门——面向对象
- 一文读懂KEGG数据库
- (创建模式 上)设计模式——工厂、抽象工厂 C++/Python3实现
- 【新手宝典】一篇博文带萌新建站并了解建站体系流程和对萌新友好的便捷方式,这篇博文很有可能是你的启蒙文
- 一种不需要敲代码的Python 画图方法
- 【一】Windows API 零门槛编程指南——MessageBox 基本使用及基础讲解
- 【二】Windows API 零门槛编程指南——CreateWindow 窗口创建 “万字长篇专业术语全解”
- 「零门槛多语言 Python/C/C# 通用思想学习系列」第一篇:经典HelloWorld
- 直播系统定制,判断数据连接是否可用
- VS Code 编辑器入门指南上篇-核心概念与组件
- Python turtle库实现基本剖析