0

Say I have 2 methods (A and B)

public void A()
{
   using (var connection = Database.GetConnectionString())
   using (var tran = new TransactionScope())
   {
       //do "A"
       tran.Complete()
   }
}

public void B()
{
   using (var connection = Database.GetConnectionString())
   using (var tran = new TransactionScope())
   {
       A(); //call "A"
       //do "B"
       tran.Complete()
   }
}

As you can see, method B need to call A, but both A and B already has its own TransactionScope, in this case, what's the correct way to pass the TransactionScope?

My Expectation is when method A is called, it will
-. BEGIN TRAN
-. DO "A"
-. COMMIT TRAN

when method B is called, it will
-. BEGIN TRAN
-. DO "A"
-. DO "B"
-. COMMIT TRAN

Any help will be appreciated

tickwave
  • 3,335
  • 6
  • 41
  • 82
  • 1
    Can [this](https://learn.microsoft.com/previous-versions/dotnet/netframework-3.0/ms172152(v=vs.85)#managing-transaction-flow-using-transactionscopeoption) help you? – Matteo Umili Jul 31 '20 at 10:58
  • 1
    Method `A(TransactionScope tran=null)` if tran == null create TransactionScope else none? – Dmitry Jul 31 '20 at 10:59

1 Answers1

2

TransactionScope is ambient and supports logical nesting by default (aka TransactionScopeOption.Required); so usually you don't really need to pass it anywhere - as long as you (or Dapper) open(s) the connection inside the scope: it should be included. When two such TransactionScope are nested: committing the inner one effectively does nothing (aborting an inner one dooms the entire transaction) - committing the outer transaction commits the entire thing.

However, frankly I'd usually advise using explicit ADO.NET transactions instead of TransactionScope, and in that case you do need to pass it in; optional parameters are your friend there.

Additionally: note that TransactionScope is almost completely separate to BEGIN TRAN / COMMIT TRAN; it is an entirely different way of representing transactions (since it needs to support distributed transactions over different systems - typically via either LTM or DTC).

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • correct me if I'm wrong, so with my current code, if I'm calling `B`, and it got to the line where `B` calls `A` and `A` is trying to do `tran.Complete`, it will basically do nothing because `A` IS NOT THE ROOT transaction, am I correct? Another question, with the way I did `using (var connection = Database.GetConnectionString())` in both method, is this going to be a problem? Since I basically has nested connection, meaning that if I'm calling `B`, and when `B` calls `A`, `A` will try to open a new connection. – tickwave Jul 31 '20 at 11:40