检查现有的TransactionScope是否已激活

10

我正在使用:

public class TransactionUtils
{
    public static TransactionScope CreateTransactionScope()
    {
        var TransactionOptions = new TransactionOptions();
        TransactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
        TransactionOptions.Timeout = TimeSpan.MaxValue;
        return new TransactionScope(TransactionScopeOption.Required, TransactionOptions);
    }
}

我使用TransactionUtils.CreateTransactionScope()来创建所有的交易。但是当我嵌套两个TransactionUtils.CreateTransactionScope()时,会出现错误:Time-out interval must be less than 2^32-2. Parameter name: dueTm。我猜测是由于它尝试将子事务附加到父事务上,导致了组合超时时间过长。

有没有一种方法可以判断新创建的事务是否是嵌套事务,以便我可以避免设置超时时间?

另一种方法是向CreateTransactionScope()传递一个参数,以便我可以告诉它这是一个嵌套事务,并且不设置超时时间,但我更希望找到自动处理它的方法。


1
我认为它选择的最大值将来自机器配置,你真的需要 TimeSpan.MaxValue 部分吗?也许这会有所帮助:http://stackoverflow.com/questions/1963934/error-time-out-interval-must-be-less-than-232-2-parameter-name-duetm - V4Vendetta
我在发布后考虑了一下,将超时时间更改为30秒,因为这样更有意义(谁想要运行数月的SQL事务呢?:))。这样“避免”了问题,但仍然很好知道我可以检查事务是否处于活动状态。 - webnoob
2个回答

35

有一个相当简单的答案:

System.Transactions.Transaction.Current != null

此功能自Framework 2.0以来可用。

我不确定为什么其他答案要深入创建链接到非公共字段的IL代码。


3
刚刚调查过(太好了,使用了ILSpy),如果你正在事务内部并且TransactionScope已完成,Current可能会抛出异常。因此请注意,你可能会收到异常而不是预期的结果。 - Roger Willcocks
@RogerWillcocks 很好的发现。我已经有一段时间没有看过IL了,而且我不确定这是在2.0框架之后添加的还是之前就有了。而且...我正在想为什么它会永远抛出异常...然而,这个答案仍然比构建动态IL来检查非公共字段要好,因为中等信任环境会更难处理。 - enorl76
我能想到的唯一可能是在调用TransactionScope.Complete()之后,尝试将其用于更多的事务活动。 - Roger Willcocks
1
@RogerWillcocks说:如果您在事务内部且TransactionScope已完成,则Transaction.Current会抛出异常。有没有任何解决方案可以在没有Transaction.Current抛出异常的情况下实现呢? - Kiquenet

1

是的,这是可能的。我有以下代码。我不是自己制作的,但忘记了从哪里得到它。向原作者致敬,但我记得我只是在谷歌上找到它。我正在使用它进行 .net 4.0,不知道它与其他版本的兼容性如何(它取决于特定程序集中的特定类)。

使用下面的代码,您可以检查在代码的某个点是否正在执行“内部”事务范围。

class TransactionScopeDetector {
    private Func<TransactionScope> _getCurrentScopeDelegate;

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

1
请查看此链接:https://dev59.com/6XNA5IYBdhLWcg3wZ81R。虽然我不确定它是否是来源,但一个令人担忧的评论指出,在4.5中,“CurrentData”已更改为“TLSCurrentData”。 - Phil Cooper
@Phil Cooper,“错误地声明了CurrentData已经改变”是怎么回事?嗯,在Framework 4.5中它已经改成了TLSCurrentData。在Framework 4.5的注释中也有确认这一点的评论。 - enorl76
@enorl76 "令人担忧"(以一种引起不安或焦虑的方式)。当时我没有看到任何事实来支持它,但那时会解释为什么我正在看的东西爆炸了,需要改变/修复。 - Phil Cooper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接