C# / EF Core / SQL Server: I am doing commits to two separate databases (on two servers) within ONE transaction. Why is this working correctly?

ubof19bj  于 2023-05-05  发布在  C#
关注(0)|答案(1)|浏览(159)

I need to do CRUD operations on two separate databases (on two separate on-premise servers) within ONE transaction. All database changes should be rolled back completely if any stored procedure or CRUD operation in the transaction fails. I was told to use TransactionScope , and that distributed transactions are not supported (which is true; I got an error "distributed transactions are not supported on this platform" when I tried using TransactionScope ).

However, I just tried nested regular transactions as shown in the code below, and it did everything I wanted. Curious to know:

  • Why does this work correctly? What am I not understanding?
  • Is the code correct for my purposes? Should I be wary of any edge cases?
// DbContexts for my 2 DBs dependency-injected
MyFirstDB _my1st_DbContext;
MySecondDB _my2nd_DbContext;
...
MyFirstDB _my1st_DbContext;
MySecondDB _my2nd_DbContext;
...
public async Task ExecuteOperations()
{
    var secondTransac = _my2nd_DbContext.Database.BeginTransaction()

    using (var firstTransac = _my1st_DbContext.Database.BeginTransaction()) 
    {
        try
        {
            await _my1st_DB_StoredProcService.GenerateStudentRecord();
 
            await _my1st_DbContext.AnimalTable.AddAsync(new Animal{Name="McDogFace"});
            await _my1st_DbContext.SaveChangesAsync();

            using (secondTransac)
            {
                await _my2nd_DbContext.VehicleTable.AddAsync(new Vehicle { Brand = "BMW" });
                await _my2nd_DbContext.SaveChangesAsync();

                // This stored procedure throws an error to roll back
                // all the DB operations done so far on both DBs
                // Note: This is a call to a my1st_DB stored proc
                await _my1st_DB_StoredProcService.GenerateEmployeeRecord();

                await secondTransac.CommitAsync();
            }
      
            await firstTransac.CommitAsync();
        }
        catch (Exception ex) 
        {
            await firstTransac.RollbackAsync();
            await secondTransac.RollbackAsync();
            throw ex.InnerException ?? new Exception(ex.Message);
        }
    }
}
kcwpcxri

kcwpcxri1#

Why does this work correctly? What am I not understanding?

You need to define "correctly" here. But this are different transactions from different DbContext 's which do not know anything about each other, so there is no reason for them not to work.

Is the code correct for my purposes? Should I be wary of any edge cases?

No. Try throwing an exception right before the await firstTransac.CommitAsync(); and check the results:

try
{
    // ...

    using (var secondTransac = _my2nd_DbContext.Database.BeginTransaction())
    {
        // ...
    }
      
    throw new Exception();
    await firstTransac.CommitAsync();
}

相关问题