事务用法

1、单库事务

单库事务是针一个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

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使用

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;
 }
 
//主db
//注入的SqlSugarClient或者SqlSugarScope我们称为主db
 
//子db 
//通过租户方法GetConnection出来的我们称为子db,用来操作当前数据库,没有租户事务相关方法
 
//主db可以用事务管理多个子db ,也可以使用 GetConnection等租户方法
 
 
 
//目前底层是业务执行成功后统一提交事务,业务只要失败全部回滚,统一回滚过程中都有有3次重试回滚
//从目前用户使用情况来看,相当稳定几乎没有一例失败的反馈
//高安全级别数据:请使用差异日志+Catch(AggregateException ex)进行补偿机质
//如果回滚失败会throw new AggregateException

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、跨方法事务

SqlSugarScope  单例模式支持跨事务方法

SqlSugarClient 需要IOC 设置 Scoped周期实现


6、CAP事务 TCC和Saga

6.1 原理讲解

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();

       }
}

6.2 CAP作者提供的集成示例

https://github.com/DotNetNext/SqlSugar/issues/1207

备份地址

https://www.donet5.com/Ask/9/23459


7、异步事务

请不要在同步方法里面写下面方代码,必须是异步方法才行 返回是要带有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不要漏掉了


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、嵌套事务(支持异步)

 9.1. 共享模式 

有外部事务,内部事务用外部事务(推荐

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

 9.2. 子事务独立 (不推荐

不推荐原因:这种很容易出现坑,比如业务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是不推荐的


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

 [ServiceFilter(typeof(TransactionFilter))]//加上这行就可以用了
 public IEnumerable<WeatherForecast> Get()
 {
        .....数据库操作...     
 }

 详细用法: https://www.donet5.com/Doc/27/2537


11、MySql注意事项 

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

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

image.png


关闭
果糖网