2

System.Transactions (TransactionScope) を使用して一連のプロセスを調整しようとしています。それぞれがデータベースの作業を行います。最終的に、すべてのプロセスは、1 つの親プロセスを介してアトミックにコミットまたはロールバックする必要があります。残念ながら、これまで試したことはありません。

私の基本的な戦略は、親プロセスで TransactionScope を実行し、それをファイルに保存して、ファイルをロードし、独自の TransactionScope 内でトランザクションを使用して親に戻る子プロセスを呼び出すことです。

しかし、これは私にはうまくいきません。最初の子の呼び出しから戻ると、親トランザクションが中止としてマークされていることが時々あります。それを複製しようとすると、TransactionAbortedException がスローされます。

2 番目の子がトランザクションを逆シリアル化しようとしたときにも例外が発生しました。コード 0x8004d00e で TransactionException が発生しました。

AppDomains とプロセス全体の TransactionScopehttp://blogs.microsoft.co.il/blogs/sasha/archive/2010/04/30/propagating-a-transaction-across-appdomainsで説明されていることを実行しようとしています。 aspx。とにかく、運が悪い。

成功せずに試したことがいくつかあります:

  1. ロードされたトランザクションから子プロセスで DependentClone() を介して DependentTransaction を作成する
  2. トランザクションをファイルに保存する前に、親プロセスで DependentClone() を介して DependentTransaction を作成する
  3. トランザクションをファイルに保存する前に、親プロセスで Clone() を作成する
  4. シリアル化を使用してトランザクションを保存する
  5. TransactionInterop.GetTransactionFromTransmitterPropagationToken() を使用してトランザクションを保存する
  6. 親の TransactionScope の前に明示的に接続を開く
  7. 親内でトランザクションを明示的に参加させる
  8. 子内でトランザクションを明示的に参加させる
  9. 親のスコープを完了する/完了しない
  10. 子のスコープを完了する/完了しない
  11. 親で CommittableTransaction を明示的に作成する

1 つの例外メッセージを次に示します。

System.Transactions.TransactionException: トランザクションは既に暗黙的または明示的にコミットまたは中止されています。---> System.Runtime.InteropServices.COMException: トランザクションは既に暗黙的または明示的にコミットまたは中止されています (HRESULT からの例外: 0x8004D00E)

もう 1 つ (DependentClone() を使用する場合):

System.Transactions.TransactionAbortedException: トランザクションが中止されました。System.Transactions.TransactionStatePromotedAborted.CreateBlockingClone (InternalTransaction tx) で System.Transactions.DependentTransaction..ctor (IsolationLevel isoLevel、InternalTransaction internalTransaction、ブール値のブロック) で System.Transactions.Transaction.DependentClone (DependentCloneOption cloneOption) で

私が間違っていることはありますか?私は運なしでこれの多くの順列を試しました。

以下にいくつかのコードを示します (上記のすべてのバリアントを示すものではありません)。

        // one variant I have tried is to create a CommittableTransaction
        // and pass that in the scope below

        using (TransactionScope scope = new TransactionScope())
        {
            // optionally, do some parent-level EF work

            // invoke child operations in other processes
            DoChildOperation_OutOfProc(1, Transaction.Current);
            DoChildOperation_OutOfProc(2, Transaction.Current);

            scope.Complete();
        }

        // in the variant where I created a CommittableTransaction,
        // I committed it here

    ...

    private static void DoChildOperation_OutOfProc(int id, Transaction transaction)
    {
        string tranFile = string.Format("ChildTran_{0}.txt", id);
        SaveTransactionToFile(transaction, tranFile);

        Process process = new Process();
        process.StartInfo = new ProcessStartInfo(Process.GetCurrentProcess().MainModule.FileName.Replace(".vshost", string.Empty),
            string.Format("-CHILDID={0} -TRANFILE={1}", id, tranFile));
        process.StartInfo.UseShellExecute = false;
        process.Start();
        process.WaitForExit();
    }

    private static void SaveTransactionToFile(Transaction transaction, string tranFile)
    {
        byte[] transactionBytes =
            TransactionInterop.GetTransmitterPropagationToken(transaction);

        string tranFileContents = Convert.ToBase64String(transactionBytes);

        File.WriteAllText(tranFile, tranFileContents);
    }

    private static Transaction LoadTransactionFromFile(string tranFile)
    {
        string tranFileContents = File.ReadAllText(tranFile);
        File.Delete(tranFile);

        byte[] tranBytes = Convert.FromBase64String(tranFileContents);

        Transaction tran = 
            TransactionInterop.GetTransactionFromTransmitterPropagationToken(tranBytes);
        return tran;
    }

    // the child instance of the app runs this after decoding the arguments
    // from DoChildOperation_OutOfProc() and loading the transaction out of the file

    private static void DoChildOperation(int id, Transaction childTransaction)
    {
        // in one variant, I call 
        // childTransaction.DependentClone(DependentCloneOption.BlockCommitUntilComplete)
        // and then use that inside the TransactionScope

        using (TransactionScope scope = new TransactionScope(childTransaction))
        {
            // do EF work and call SaveChanges()

            scope.Complete();
        }

        // if I created a dependent clone, call Complete() here on it
4

1 に答える 1

1

さて、重要なのは、親では TransactionScope を使用できますが、子では使用できないことです。子の場合、EF 接続を開き、渡されたトランザクションで connection.EnlistTransaction() を呼び出し、EF SaveChanges() または transaction.Rollback() を実行しますが、コミットはしません (Transaction クラスはこれを提供しません)。このようにすると、目的の動作が得られるようです。

私の理解のギャップは、トランザクションが入れ子になっている (SQL Server でできるように) かどうかでした。実際にはそうではないようです。同じ取引です。注: 子で Transaction.DependentClone() を使用して DependentTransaction を作成しても、それを TransactionScope に入れると失敗します。

これは、プロセスが親の場合は TransactionScope を使用できますが、子の場合は使用できないことを意味するため、少し残念です。

于 2012-11-27T22:14:35.760 に答える