Подтвердить что ты не робот

Как узнать, находится ли код внутри TransactionScope?

Каков наилучший способ узнать, находится ли блок кода внутри TransactionScope?
Является ли Transaction.Current реальным способом сделать это или есть какие-то тонкости?
Возможно ли получить доступ к внутреннему ContextData.CurrentData.CurrentScope(в System.Transactions) с отражением? Если да, то как?

4b9b3361

Ответ 1

Вот более надежный способ (как я уже сказал, Transaction.Current можно установить вручную, и это не всегда означает, что мы действительно находимся в TransactionScope). Также можно получить эту информацию с отражением, но излучение IL работает в 100 раз быстрее, чем отражение.

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);
}

Ответ 2

Transaction.Current должен быть надежным; Я только что проверил, при этом отлично работает и с подавленными транзакциями:

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