Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to know if the code is inside TransactionScope?

What is the best way to know if the code block is inside TransactionScope?
Is Transaction.Current a realiable way to do it or there are any subtleties?
Is it possible to access internal ContextData.CurrentData.CurrentScope (in System.Transactions) with reflection? If yes, how?

like image 825
nightcoder Avatar asked Jun 11 '09 10:06

nightcoder


People also ask

What is TransactionScope in C#?

The TransactionScope class provides a simple way to mark a block of code as participating in a transaction, without requiring you to interact with the transaction itself. A transaction scope can select and manage the ambient transaction automatically.

What is an ambient transaction?

Ambient TransactionA transaction which automatically identifies a code block that needs to support a transaction without explicitly mentioning any transaction related things. An ambient transaction is not tied just to a database, any transaction aware provider can be used.

What is transaction scope in SQL Server?

Definition: TransactionalScope makes your code block Transactional. You can easily maintain one transaction for multiple databases or a single database with multiple connectionstrings, using TransactionScope. When you use TransactionScope there is no need to close any Database connections in the middle.


2 Answers

Transaction.Current should be reliable; I've just checked, at this works fine with suppressed transactions, too:

Console.WriteLine(Transaction.Current != null); // false using (TransactionScope tran = new TransactionScope()) {     Console.WriteLine(Transaction.Current != null); // true     using (TransactionScope tran2 = new TransactionScope(           TransactionScopeOption.Suppress))     {         Console.WriteLine(Transaction.Current != null); // false     }     Console.WriteLine(Transaction.Current != null); // true } Console.WriteLine(Transaction.Current != null); // false 
like image 167
Marc Gravell Avatar answered Sep 27 '22 21:09

Marc Gravell


Here is more reliable way (as I said, Transaction.Current can be set manually and it doesn't always mean we are really in TransactionScope). It's also possible to get this information with reflection, but emiting IL works 100 times faster than reflection.

private Func<TransactionScope> _getCurrentScopeDelegate;  bool IsInsideTransactionScope {   get   {     if (_getCurrentScopeDelegate == null)     {       _getCurrentScopeDelegate = CreateGetCurrentScopeDelegate();     }      TransactionScope ts = _getCurrentScopeDelegate();     return ts != null;   } }  private Func<TransactionScope> CreateGetCurrentScopeDelegate() {   DynamicMethod getCurrentScopeDM = new DynamicMethod(     "GetCurrentScope",     typeof(TransactionScope),     null,     this.GetType(),     true);    Type t = typeof(Transaction).Assembly.GetType("System.Transactions.ContextData");   MethodInfo getCurrentContextDataMI = t.GetProperty(     "CurrentData",      BindingFlags.NonPublic | BindingFlags.Static)     .GetGetMethod(true);    FieldInfo currentScopeFI = t.GetField("CurrentScope", BindingFlags.NonPublic | BindingFlags.Instance);    ILGenerator gen = getCurrentScopeDM.GetILGenerator();   gen.Emit(OpCodes.Call, getCurrentContextDataMI);   gen.Emit(OpCodes.Ldfld, currentScopeFI);   gen.Emit(OpCodes.Ret);    return (Func<TransactionScope>)getCurrentScopeDM.CreateDelegate(typeof(Func<TransactionScope>)); }  [Test] public void IsInsideTransactionScopeTest() {   Assert.IsFalse(IsInsideTransactionScope);   using (new TransactionScope())   {     Assert.IsTrue(IsInsideTransactionScope);   }   Assert.IsFalse(IsInsideTransactionScope); } 
like image 26
nightcoder Avatar answered Sep 27 '22 19:09

nightcoder