事务用法

1、单库事务

单库事务是针一个db操作执行的事务,无论是 ISqlSugarClient和  SqlSugarClient 用法都一样

    try
    {
        db.Ado.BeginTran();
        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

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 //多库

2.1 SqlSugarClient事务

  因为继承  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;
 }

2.2 ISqlSugarClient事务

因为和ITenant没有继承关需要转换一下 

db.AsTenant().BeginTran();//低版本 (db as ITenant).BeginTran() 
db.AsTenant().CommitTran();
db.AsTenant().RollbackTran();


3、调试事务

db.ContextId要从事务开始,CURD 和事务结束 必须一致 这个事务才会生效,如果是MYSQL也检查一下表引擎是否支持事务

不一致怎么办?

  1. SqlsugarClient 可以用变量 var db=外部Db; 所有操作使用db保证一致 

  2. SqlsuagrScope (该对象是线程安全对象,可以单例)可以用单例模式保证一致

image.png

测试代码 最好用 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());//回滚后数量
            }

输出结果 插入前和回滚后一样就说明成功的

image.png

4、语法糖

语法糖1: 5.0.4才支持

这种适合有全局异常的,直接出错扔出错并且回滚

   using (var tran = db.UseTran()){

        //业务代码
        //里面禁止写 try处理事务逻辑,格式一定按现在的风格来

        tran.CommitTran();
     }
     //要写 try处理异常可以写在外面

语法糖2:自动异常

这种适合没有异常处理的,减少了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)//事务提交完成
    }

语法糖3:工作单元

UnitOfWork:  https://www.donet5.com/Home/Doc?typeId=2360


5、跨方法事务

3.1 我们可以用 SqlSugarScope 单例模式 来实现事务跨方法 

https://www.donet5.com/Home/Doc?typeId=2352


6、CAP事务 TCC和Saga

CAP可以支持跨程序间的事务处理(非跨程序事务不建议用,涉及到队列等,在单程序中稳定性肯定不如自带的多租户事务

注意: MySql用户使用 升级到最新 

1、数据库的自动释放要关闭

2、手动打开数据库连接 db.Ado.Connection.Open();

3、用db.Ado.Connection创建事务

4、把你的事务赋值到ORM对象  db.Ado.Transaction = 你的事务;

5、执行你的代码

6、关闭Connection对象

//用户使用案例
var db=GetSqlsugarclient();//关闭自动释放
using (var connection = (MySqlConnection)db.Ado.Connection)
{
     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();

       }
}


7、异步事务

请不要在同步方法里面写下面方代码,必须是异步方法才行 返回是要带有Task async

用法1:

  try
     {
          await db.BeginTranAsync();//老版本用db.BeginTran()新版本用了MySqlConnector必须要用异步事务

              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不要漏掉了


8、设置事务隔离级别

单库模式用法

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;
}

9、工作单元【嵌套事务】

通过工作单元实现嵌套事务  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会自动回滚
     }


10、工作单元【事务特性】

在 Furion 框架中完美支持SqlSugar工作单元特性,我们只需要在控制器 Action 中贴 [UnitOfWork] 特性即可开启工作单元模式,保证了每一次请求都是一个 工作单元,要么同时成功,要么同时失败。 

[UnitOfWork]    // 由于出现错误,所以所有数据库变更都会自动回滚
public async Task 测试环境事务(int id)
{
    // 各种奇葩数据库操作
    await _personRepository.DeleteNowAsync(id);
    // 其他数据库操作。。
    // 故意出错
    var d = await _personRepository.SqlQueriesAsync("select * from persion2 d");
}

下载DEMO:

 

SqlSugarTran.zip


11、MySql注意事项 

(1)MYSQL不支持创建表和删除表处理事务,原生事务也一样 

(2) MyISAM 存储引擎不支持事务 需要改成 InnoDB 

image.png


文档:SqlSugar5.0