【问题标题】:How to know if the code is inside TransactionScope?如何知道代码是否在 TransactionScope 内?
【发布时间】:2009-06-11 10:14:26
【问题描述】:

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

【问题讨论】:

    标签: c# .net transactionscope


    【解决方案1】:

    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
    

    【讨论】:

    • 我的意思是即使我们不在 TransactionScope 中也可以设置 Transaction.Current 属性。
    • 如果 TransactionScope 已完成,但仍未释放,则 Syste.Transactions.Transaction.Curre 抛出异常
    【解决方案2】:

    这是更可靠的方法(正如我所说,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);
    }
    

    【讨论】:

    • 不知道您在生产环境中使用此代码四年后是否改变了“可靠”的定义。
    • 如果 Transaction.Current 不可靠,为什么 .Net 开发人员不将其保留为只读?你看过它的实现吗?
    • 在.Net 4.5中出现“CurrentData”已重命名为“TLSCurrentData”
    • @JeremyRosenberg 我同意。今天是我第二次遇到巫毒代码反映框架内部的魔法字符串,只是为了改变它们并随之而来。
    • @ChrisMcKelt 你在哪里看到的?网上有没有关于这个变化的参考?
    【解决方案3】:

    有使用不需要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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 1970-01-01
      • 1970-01-01
      • 2014-07-17
      • 1970-01-01
      相关资源
      最近更新 更多