【问题标题】:TransactionScope throws TransactionAbortedException when DisposedTransactionScope 在 Disposed 时抛出 TransactionAbortedException
【发布时间】:2014-12-11 10:24:34
【问题描述】:

我有以下场景:

  • 父进程启动 TransactionScope,使用 TransactionInterop.GetTransmitterPropagationToken 创建标识事务的令牌,将数据插入数据库。 TransactionScope 完成。
  • 另一个进程启动,使用上面提到的令牌创建事务,然后用于创建事务范围。此过程还将数据插入数据库。 TransactionScope 完成并被释放。
  • 此时父进程尝试释放其 TransactionScope,并抛出 TransactionAbortedException。该异常并未给出无法提交的任何理由。

堆栈跟踪:

at System.Transactions.TransactionStatePromotedAborted.BeginCommit(InternalTransaction tx, Boolean asyncCommit, AsyncCallback asyncCallback, Object asyncState)
at System.Transactions.CommittableTransaction.Commit()
at System.Transactions.TransactionScope.InternalDispose()
at System.Transactions.TransactionScope.Dispose()
at DistributedTransactions.Program.Main() in c:\Users\agolan.ALLSHARE\Documents\Visual Studio 2013\Projects\DistributedTransactions\DistributedTransactions\Program.cs:line 44
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

这是应用程序的代码:

internal class Program {
    private static void Main() {
        string connString = "data source=.;initial catalog=Test;integrated security=True;persist security info=True";
        string tokenFile = @"c:\Temp\token.txt";

        Transaction transaction = null;
        bool isChild = false;
        if (File.Exists(tokenFile)) {
            isChild = true;
            string tokenString = File.ReadAllText(tokenFile);
            byte[] token = Convert.FromBase64String(tokenString);
            transaction = TransactionInterop.GetTransactionFromTransmitterPropagationToken(token);
        }
        using (var transactionScope = transaction != null ? new TransactionScope(transaction) : new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 15, 0))) {
            var curr = Transaction.Current;
            if (!isChild) {
                byte[] transactionBytes = TransactionInterop.GetTransmitterPropagationToken(curr);

                string tokenString = Convert.ToBase64String(transactionBytes);
                File.WriteAllText(tokenFile, tokenString);
            }
            using (var conn = new SqlConnection(connString)) {
                conn.Open();
                using (SqlCommand cmd = conn.CreateCommand()) {
                    Console.WriteLine("Enter id and value");
                    cmd.CommandText = "INSERT INTO KeyValue(Id, Value) VALUES (@1, @2)";
                    cmd.Parameters.Add(new SqlParameter("@1", Console.ReadLine()));
                    cmd.Parameters.Add(new SqlParameter("@2", Console.ReadLine()));
                    cmd.ExecuteNonQuery();
                }
            }
            transactionScope.Complete();

            Console.WriteLine("Dispose");
            Console.ReadLine();
        }
    }
}

问题:

  • 为什么不能提交?
  • 在这种情况下是否可以使用 TransactionScope(我阅读了答案 Using transactions across processes 但我想使用 TransactionScope)?怎么样?

【问题讨论】:

  • 异常发生前第二个进程是否还在运行?
  • @hjb417 不,当异常发生时它没有运行(就像我在这个过程的描述 TransactionScope 中提到的那样完成并被处置)。所以一旦第二个完成,我尝试完成第一个,然后......异常。
  • 如果第二个进程仍在运行,问题是否仍然存在?
  • @hjb417 确实,第二个进程的终止是问题!

标签: c# .net transactions msdtc system.transactions


【解决方案1】:

我能够在本地复制它,这是因为在父进程提交事务之前另一个应用程序已经退出。其他应用程序必须继续运行,直到父应用程序提交或回滚事务。一种方法是阻止应用程序退出,直到触发 TransactionCompleted 事件。

我修改了您的代码以使用ManualResetEventSlim 使子进程在退出之前等待父事务进程完成。

internal class Program
{
    private static void Main()
    {
        string connString = "data source=.;initial catalog=Test;integrated security=True;persist security info=True";
        string tokenFile = @"c:\Temp\token.txt";

    Transaction transaction = null;
    bool isChild = false;
    if (File.Exists(tokenFile))
    {
        isChild = true;
        string tokenString = File.ReadAllText(tokenFile);
        byte[] token = Convert.FromBase64String(tokenString);
        transaction = TransactionInterop.GetTransactionFromTransmitterPropagationToken(token);
    }
    using (var parentTxCompleteEvent = new ManualResetEventSlim(!isChild))
    {
        using (var transactionScope = transaction != null ? new TransactionScope(transaction) : new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 15, 0)))
        {
            var curr = Transaction.Current;
            if (!isChild)
            {
                byte[] transactionBytes = TransactionInterop.GetTransmitterPropagationToken(curr);

                string tokenString = Convert.ToBase64String(transactionBytes);
                File.WriteAllText(tokenFile, tokenString);
            }
            else
            {
                transaction.TransactionCompleted += (sender, e) => parentTxCompleteEvent.Set();
            }
            using (var conn = new SqlConnection(connString))
            {
                conn.Open();
                using (SqlCommand cmd = conn.CreateCommand())
                {
                    Console.WriteLine("Enter id and value");
                    cmd.CommandText = "INSERT INTO KeyValue(Id, Value) VALUES (@1, @2)";
                    cmd.Parameters.Add(new SqlParameter("@1", Console.ReadLine()));
                    cmd.Parameters.Add(new SqlParameter("@2", Console.ReadLine()));
                    cmd.ExecuteNonQuery();
                }
            }
            transactionScope.Complete();

            Console.WriteLine("Dispose");
            Console.ReadLine();
        }
        parentTxCompleteEvent.Wait();
    }
}

}

【讨论】:

    猜你喜欢
    • 2013-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多