0

次のシナリオがあります。

  • 親プロセスが TransactionScope を開始します。トランザクションを識別するトークンは、TransactionInterop.GetTransmitterPropagationToken を使用して作成され、データベースにデータを挿入します。TransactionScope が完了します。
  • 上記のトークンを使用して別のプロセスが開始され、TransactionScope の作成に使用される Transaction が作成されます。このプロセスは、データをデータベースに挿入します。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 を使用することは可能ですか? どのように?
4

1 に答える 1

2

これをローカルに複製することができました。これは、親プロセスがトランザクションをコミットする前に他のアプリケーションが終了したために発生します。親がトランザクションをコミットまたはロールバックするまで、他のアプリケーションは実行を継続する必要があります。これを行う 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(); } }

}

于 2014-12-29T06:19:09.333 に答える