32

知道代码块是否在 TransactionScope 内的最佳方法是什么?
Transaction.Current 是一种可行的方法还是有什么微妙之处?
是否可以通过反射访问内部 ContextData.CurrentData.CurrentScope(在 System.Transactions 中)?如果是,如何?

4

3 回答 3

46

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
于 2009-06-11T10:20:49.550 回答
7

这是更可靠的方法(正如我所说,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);
}
于 2009-07-03T15:35:37.193 回答
1

有使用不需要System.Transactions参考的表达式的更新版本。

internal static class TransactionScopeHelper
{
    static Func<object?> _getCurrentScopeDelegate = GetTransactionScopeFunc();

    public static bool IsInsideTransactionScope
    {
        get
        {
            var ts = _getCurrentScopeDelegate();
            return ts != null;
        }
    }

    static Func<object?> GetTransactionScopeFunc()
    {
        var assembly = AppDomain.CurrentDomain.GetAssemblies()
            .FirstOrDefault(a => a.GetName().Name == "System.Transactions");

        if (assembly != null)
        {
            var t = assembly.GetType("System.Transactions.ContextData");
            var currentDataProperty = t.GetProperty("TLSCurrentData", BindingFlags.NonPublic | BindingFlags.Static);
            if (currentDataProperty != null)
            {
                var body   = Expression.MakeMemberAccess(null, currentDataProperty);
                var lambda = Expression.Lambda<Func<object?>>(body);
                return lambda.Compile;
            }
        }

        return () => null;
    }
}
于 2020-09-09T14:33:06.027 回答