事务的使用


  Fireasy 的事务是一个 Scope 级别的,即在一次请求期间(即使异步也是),一旦开启了事务,使用相同连接串的不同 DatabaseEntityContext 均会使用同一个事务对象,且事务的提交或回滚只会在开启事务的代码块内有效,这就避免了事务嵌套所带来的误提交或回滚问题。在 EntityContext 里使用事务,有以下几种方法。


1、Begin / Commit / Rollback

  使用 BeginTransaction 开启一个事务,如果此时已经有一个事务对象,则此操作将被忽略。使用 CommitTransaction 方法提交一个事务,如果当前的事务对象不是由此 EntityContext 所开启的,则此操作也将忽略。使用 RollbackTransaction 方法回滚事务,其规则与 CommitTransaction 一样。

  标准调用示例如下:

[TestMethod]
public void TestTrans()
{
    using (var db = new DbContext())
    {
        try
        {
            //事务开启
            db.BeginTransaction();
            
            //其他处理
                
            //事务提交
            db.CommitTransaction();
        }
        catch
        {
            db.RollbackTransaction();
        }
    }
}

  下面的示例是事务的嵌套情况,DbContext1DbContext2 使用不同的数据库连接。

[TestMethod]
public void TestTrans()
{
    using (var db = new DbContext1())
    {
        //事务开启
        db.BeginTransaction();
                
        TestNestedTrans1();
                
        //事务提交
        db.CommitTransaction();
    }
}

private void TestNestedTrans1()
{
    using (var db = new DbContext1())
    {
        //由于与 TestTrans 使用的是同一个连接,因此此方法里的 Begin 和 Commit 均被忽略
        db.BeginTransaction();

        TestNestedTrans2();
        TestNestedTrans3();

        db.CommitTransaction();
    }
}

private void TestNestedTrans2()
{
    using (var db = new DbContext2())
    {
        //事务开启
        db.BeginTransaction();

        db.Database.ExecuteNonQuery((SqlCommand)"delete from orders where orderid=-1");

        //事务提交
        db.CommitTransaction();
    }
}

private void TestNestedTrans3()
{
    using (var db = new DbContext1())
    {
        //由于与 TestTrans 使用的是同一个连接,因此此方法里的 Begin 和 Commit 均被忽略
        db.BeginTransaction();

        db.Database.ExecuteNonQuery((SqlCommand)"delete from orders where orderid=-1");

        db.CommitTransaction();
    }
}


2、EntityTransactionScope

  可以使用 EntityTransactionScope 统一来开启事务,它会管理不同数据库连接的事务对象,最后调用 Complete 方法提交所有的数据库事务。

[TestMethod]
public async Task TestTransScope()
{
    using (var scope = new EntityTransactionScope())
    {
        using (var db = new DbContext1())
        {
            await db.Products.DeleteAsync(s => false);
        }
        using (var db = new DbContext2())
        {
            await db.Products.DeleteAsync(s => false);
        }

        scope.Complete();
    }
}

3、UseTransaction 扩展方法

  EntityContext 有一组扩展方法用于开启事务并将事务体放在委托里,它实际上是对 Begin / Commit / Rollback 的包装。

[TestMethod]
public void TestTrans()
{
    using (var db = new DbContext())
    {
        //事务开启
        db.UseTransaction(db =>
        {
            //其他处理
        }, throwExp: exp => 
        {
            //异常处理
        });
    }
}

4、UnitOfWork

  //todo 暂未提供。