- 
                Notifications
    You must be signed in to change notification settings 
- Fork 891
        28810 edited this page May 23, 2020 
        ·
        73 revisions
      
    using (var uow = fsql.CreateUnitOfWork()) {
  var songRepo = fsql.GetRepository<Song>();
  var userRepo = fsql.GetRepository<User>() 
  songRepo.UnitOfWork = uow;
  userRepo.UnitOfWork = uow;
  songRepo.Insert(new Song());
  userRepo.Update(...);
  uow.Commit();
}参考:在 asp.net core 中使用 TransactionalAttribute + UnitOfWorkManager 实现多种事务传播
using (var ctx = fsql.CreateDbContext()) {
  var song = ctx.Set<Song>();
  var user = ctx.Set<User>();
  
  song.Add(new Song());
  user.Update(...);
  ctx.SaveChanges();
}同线程事务,由 fsql.Transaction 管理事务提交回滚(缺点:不支持异步),比较适合 WinForm/WPF UI 主线程使用事务的场景。
用户购买了价值100元的商品:扣余额、扣库存。
fsql.Transaction(() =>  {
  //fsql.Ado.TransactionCurrentThread 获得当前事务对象
  var affrows = fsql.Update<User>()
    .Set(a => a.Wealth - 100)
    .Where(a => a.Wealth >= 100).ExecuteAffrows();
    //判断别让用户余额扣成负数
    
  if (affrows < 1)
    throw new Exception("用户余额不足");
    //抛出异常,回滚事务,事务退出
  affrows = fsql.Update<Goods>()
    .Set(a => a.Stock - 1)
    .Where(a => a.Stock >= 1).ExecuteAffrows();
    //判断别让用库存扣成负数
    
  if (affrows < 1)
    throw new Exception("商品库存不足");
    //抛出异常,回滚事务,事务退出
});同线程事务使用简单,需要注意的限制:
- 
事务对象在线程挂载,每个线程只可开启一个事务连接,嵌套使用的是同一个事务; 
- 
事务体内代码不可以切换线程,因此不可使用任何异步方法,包括FreeSql提供的数据库异步方法(可以使用任何 Curd 同步方法); 
- 
fsql.Transaction 有防止死锁机制,60秒事务未结束的,将会被其他线程强行提交(不是回滚),可能造成不完整的事务,但仔细一想60秒还没完成的事务是什么原因呢?如果嫌60秒太少了可以在重载方法的参数中设置; 
在外部开启事务的场景,可使用 WithTransaction 传入事务对象。
await fsql.Update<xxx>()
  .WithTransaction(指定事务)
  .Set(a => a.Clicks + 1)
  .ExecuteAffrowsAsync();ISelect、IInsert、IUpdate、IDelete,都支持 WithTransaction 方法。
var user = fsql.Select<User>().ForUpdate(true).Where(a => a.Id == 1).ToOne();
//SELECT ... FROM User a for update nowaitfor update 在 Oracle/PostgreSQL/MySql 是通用的写法,我们对 SqlServer 做了特别适配,执行的 SQL 语句大致如下:
SELECT ... FROM [User] a With(UpdLock, RowLock, NoWait)fsql.Transaction(() => //全局线程事务
{
  fsql.Insert(new Products()).ExecuteAffrows();
  fsql.Insert(new Products()).ExecuteAffrows();
});
using (var uow = fsql.CreateUnitOfWork()) //使用 UnitOfWork 事务
{
  fsql.Insert(new Products()).ExecuteAffrows(); //错误,没有传事务
  fsql.Insert(new Products()).WithTransaction(uow.GetOrBeginTransaction()).ExecuteAffrows();
  fsql.Insert(new Products()).WithTransaction(uow.GetOrBeginTransaction()).ExecuteAffrows();
  var repo1 = uow.GetRepository<Products>();
  repo1.Insert(new Products());
}
using (var ctx = fsql.CreateDbContext()) //使用 DbContext 事务
{
  ctx.Add(new Products());
  ctx.Add(new Products());
  fsql.Insert(new Products()).ExecuteAffrows(); //错误,没有传事务
  fsql.Insert(new Products()).WithTransaction(ctx.UnitOfWork.GetOrBeginTransaction()).ExecuteAffrows(); //正常
}
using (var uowManager = new UnitOfWorkManager(fsql)) //使用 UnitOfWorkManager 管理类事务
{
  using (var uow = uowManager.Begin())
  {
    fsql.Insert(new Products()).ExecuteAffrows(); //错误,没有传事务
    fsql.Insert(new Products()).WithTransaction(uow.GetOrBeginTransaction()).ExecuteAffrows();
    fsql.Insert(new Products()).WithTransaction(uow.GetOrBeginTransaction()).ExecuteAffrows();
    using (var uow2 = uowManager.Begin()) //与 uow 同一个事务
    {
      var repo1 = fsql.GetRepository<Products>();
      repo1.UnitOfWork = uow2;
      repo1.Insert(new Products());
      uow2.Commit();//事务还未提交
    }
    uow.Commit();//事务提交
  }
}- IFreeSql curd 方法需要指定 WithTransaction 传递事务;
- FreeSql.IBaseRepository curd 方法需要指定 UnitOfWork 传递工作单元事务;
- FreeSql.DbContext 自带事务;
- UnitOfWorkManager 适合做跨方法事务;