单库事务是针一个db操作执行的事务,无论是 ISqlSugarClient和 SqlSugarClient 用法都一样
try { db.Ado.BeginTran(); db.Insertable(new Order() { .....}).ExecuteCommand(); db.Insertable(new Order() { .....}).ExecuteCommand(); db.Ado.CommitTran(); } catch (Exception ex) { db.Ado.RollbackTran(); throw ex; }
如果一个db就一个库,那么你也可以用多租户事务节约代码,因为2者在一个库的情况下作用一样
db.BeginTran();//去掉了.ado db.CommitTran();//去掉了.ado db.RollbackTran();//去掉了.ado //ISqlSugarClient 接口使用多租户事务 看文档2.2
多数据库事务是SqlSugar独有的功能,稳定比CAP更强(CAP还有一层队列),在单个程序中可以很愉快的使用多库事务
SqlSugarClient或者SqlSugarSope 继承于2个接口 ,代码如下事物
SqlSugarClient : ISqlSugarClient, ITenant
多租户声明
SqlSugarClient db = new SqlSugarClient(new List<ConnectionConfig>() { new ConnectionConfig() { ConfigId="0", DbType=DbType.SqlServer,ConnectionString=..,IsAutoCloseConnection=true}, new ConnectionConfig() { ConfigId="1", DbType=DbType.MySql,ConnectionString=..,IsAutoCloseConnection=true} });
简单的说多租户事务和单库事务用法基本100%一致,唯一区别就是少了.Ado
db.Ado.BeginTran//单库 db.BeginTran //多库事务 db.AsTenant().BeginTran()//多库事务 一般是接口ISqlSugarClient使用
因为继承 ITenant 了可以直接使用 (老版本var mysql=db.GetConnection要写在事务外面)
//禁止使用 db.Ado.BeginTran,多租户是db.BeginTran try { db.BeginTran(); db.GetConnection("1").Insertable(new Order() { }).ExecuteCommand(); db.GetConnection("0").Insertable(new Order() { }).ExecuteCommand(); db.CommitTran(); } catch (Exception) { db.RollbackTran();//数据回滚 throw; } //主db //注入的SqlSugarClient或者SqlSugarScope我们称为主db //子db //通过租户方法GetConnection出来的我们称为子db,用来操作当前数据库,没有租户事务相关方法 //主db可以用事务管理多个子db ,也可以使用 GetConnection等租户方法 //目前底层是业务执行成功后统一提交事务,业务只要失败全部回滚,统一回滚过程中都有有3次重试回滚 //从目前用户使用情况来看,相当稳定几乎没有一例失败的反馈 //高安全级别数据:请使用差异日志+Catch(AggregateException ex)进行补偿机质 //如果回滚失败会throw new AggregateException
因为和ITenant没有继承关需要转换一下
db.AsTenant().BeginTran();//低版本 (db as ITenant).BeginTran() db.AsTenant().CommitTran(); db.AsTenant().RollbackTran();
db.ContextId要从事务开始,CURD 和事务结束 必须一致 这个事务才会生效,如果是MYSQL也检查一下表引擎是否支持事务
不一致怎么办?
SqlsugarClient 可以用变量 var db=外部Db; 所有操作使用db保证一致
SqlsuagrScope (该对象是线程安全对象,可以单例)可以用单例模式保证一致
测试代码 最好用 Insert ,因为Update有条件过滤等因素添会添加测试难度,我们用插入来进行测试会比较简单些
try { db.BeginTran(); Console.WriteLine(db.Queryable<Order>().Count());//插入前数量 db.Insertable(new Order() { CreateTime=DateTime.Now, Name="aa",Price=1}).ExecuteCommand(); Console.WriteLine(db.Queryable<Order>().Count());//插入后数量 throw new Exception("出错"); db.CommitTran(); } catch { db.RollbackTran(); Console.WriteLine(db.Queryable<Order>().Count());//回滚后数量 }
输出结果 插入前和回滚后一样就说明成功的
这种适合有全局异常的,直接出错扔出错并且回滚
using (var tran = db.UseTran()){ //业务代码 //里面禁止写 try处理事务逻辑,格式一定按现在的风格来 tran.CommitTran(); } //要写 try处理异常可以写在外面
这种适合没有异常处理的,减少了try 处理
var result = db.UseTran(() => { var beginCount = db.Queryable<Order>().ToList(); db.Ado.ExecuteCommand("delete Order"); var endCount = db.Queryable<Order>().Count(); return true;// 返回值等行result.Data }); if (result.Data==false) //返回值为false { //result.Data 业务的返回值 //如果是上面的逻辑 result.Data==true肯定业务成功并且事务成功 //if(result.IsSuccess==false)//事务执行了回滚 //if(result.IsSuccess==true)//事务提交完成 }
UnitOfWork: https://www.donet5.com/Home/Doc?typeId=2360
SqlSugarScope 单例模式支持跨事务方法
SqlSugarClient 需要IOC 设置 Scoped周期实现
CAP可以支持跨程序间的事务处理(非跨程序事务不建议用,涉及到队列等,在单程序中稳定性肯定不如自带的多租户事务)
注意: MySql用户使用 升级到最新
1、数据库的自动释放要关闭
2、手动打开数据库连接 db.Ado.Connection.Open();
3、用db.Ado.Connection创建事务
4、把你的事务赋值到ORM对象 db.Ado.Transaction = 你的事务;
5、执行你的代码
6、关闭Connection对象
//用户使用案例 var db=GetSqlsugarclient(); //关闭自动释放(需要 using手动释放) db.CurrentConnectionConfig.IsAutoCloseConnection = false; //取出ADO事务 using (var connection = (MySqlConnection)db.Ado.Connection)//SqlServer是SqlConnection { using (var transaction = connection.BeginTransaction(_capBus, autoCommit: false)) { if (connection.State != ConnectionState.Open) { connection.Open(); } db.Ado.Transaction = (IDbTransaction)transaction.DbTransaction;//这行很重要 db.Insertable<Test>(new Test() { name = DateTime.Now.ToString() }).ExecuteCommand(); _capBus.Publish("Sample.RabbitMQ.MySql", DateTime.Now); transaction.Commit(); } }
https://github.com/DotNetNext/SqlSugar/issues/1207
备份地址
https://www.donet5.com/Ask/9/23459
请不要在同步方法里面写下面方代码,必须是异步方法才行 返回是要带有Task async
用法1:
try { await db.BeginTranAsync(); await db.Insertable(new Order() { CreateTime = DateTime.Now, CustomId = 1, Name = "aaa", Price = 0 }).ExecuteCommandAsync(); await db.CommitTranAsync(); } catch (Exception) { await db.RollbackTranAsync(); }
用法2:
//只有5.0.3.8支持,老版本请升级使用 var res = await db.UseTranAsync(async () => { await db.Insertable(new Order() { CreateTime = DateTime.Now, CustomId = 1, Name = "aaa", Price = 0 }).ExecuteCommandAsync(); return true;//返回值会变成 res.Data }); if (result.Data==false) //返回值为false { //result.Data 业务的返回值 //如果是上面的逻辑 result.Data==true肯定业务成功并且事务成功 //if(result.IsSuccess==false)//事务执行了回滚 //if(result.IsSuccess==true)//事务提交完成 } //注意: //await db.UseTranAsync 前面的await不要漏掉了
单库模式用法
try { db.Ado.BeginTran(IsolationLevel.ReadCommitted); //业务代码 db.Ado.CommitTran(); } catch (Exception ex) { db.RollbackTran(); throw ex; }
多租户模式
var mysqlDb = db.GetConnection("mysql"); var mssqlDb = db.GetConnection("mssql"); try { mysqlDb.Ado.BeginTran(IsolationLevel.ReadCommitted);//开启库1的事务 mssqlDb.Ado.BeginTran(IsolationLevel.ReadCommitted);//开启库2的事务 //业务代码 只能用 mysqlDb和 mssqlDb db.CommitTran();//注意不能用db.ado.CommitTran } catch (Exception ex) { db.RollbackTran(); throw ex; }
有外部事务,内部事务用外部事务(推荐)
通过工作单元实现嵌套事务 5.1.2.5-preview03
//db.Ado.IsNoTran()表示事务为null才开启事务 using (var uow = db.CreateContext(db.Ado.IsNoTran())) { using (var uow2 = db.CreateContext(db.Ado.IsNoTran())) { uow2.Commit(); } uow.Commit(); //禁止用 db.RollBack 工作单元内只要throw会自动回滚 }
不推荐原因:这种很容易出现坑,比如业务A和业务B都用到了一样的表就会死锁
try { db.BeginTran(); //业务..A var childDb=db.CopyNew(); try { childDb.BeginTran(); //...业务B childDb.Commit(); } catch (Exception) { childDb.RollbackTran();//数据回滚 throw; } db.CommitTran(); } catch (Exception) { db.RollbackTran();//数据回滚 throw; }
事务嵌套:推荐用9.1或者标题10 ,9.2是不推荐的
[ServiceFilter(typeof(TransactionFilter))]//加上这行就可以用了 public IEnumerable<WeatherForecast> Get() { .....数据库操作... }
详细用法: https://www.donet5.com/Doc/27/2537
(1)MYSQL不支持创建表和删除表处理事务,原生事务也一样
(2) MyISAM 存储引擎不支持事务 需要改成 InnoDB
2016 © donet5.comApache Licence 2.0