TransactionScopeを非同期で動作するようにする/待つ


114

私たちのサービスバスにasync/ を統合しようとしawaitています。SingleThreadSynchronizationContextこの例に基づいてhttp://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspxを実装しました。

そして、それは一つのことを除いて、うまく働きます:TransactionScope。中の物を待つTransactionScopeと壊れますTransactionScope

TransactionScopeasync/ awaitを使ってスレッドに物を保存するため、/ はうまく機能しないようですThreadStaticAttribute。私はこの例外を受け取ります:

「TransactionScopeが正しくネストされていません。」

TransactionScopeタスクをキューに入れる前にデータを保存し、実行する前に復元しようとしましたが、変更されていないようです。そしてTransactionScopeコードは混乱しているので、そこで何が起こっているのかを理解するのは本当に難しいです。

それを機能させる方法はありますか?に代わるものはありTransactionScopeますか?


これは、TransactionScopeエラーpastebin.com/Eh1dxG4aを再現するための非常に単純なコードです。ただし、ここでは例外がトランザクションの中止です
Yann

通常のSQLトランザクションだけを使用できますか?または、複数のリソースにまたがっていますか?
Marc Gravell

複数のリソースにまたがっています
Yann

スコープを非同期メソッドに渡すか、ワークユニットで識別されるある種の一般的なコンテキストからスコープを取得する方法を提供する必要があるようです。
Bertrand Le Roy

SingleThreadSynchronizationContextトップレベルごとに独自のスレッドが必要ですTransactionScope
Stephen Cleary

回答:


161

.NET Framework 4.5.1では、パラメーターを取るためTransactionScope新しいコンストラクターのセットがありTransactionScopeAsyncFlowOptionます。

MSDNによると、スレッドの継続全体でトランザクションフローを有効にします。

私の理解では、次のようなコードを記述できるようになっています。

// transaction scope
using (var scope = new TransactionScope(... ,
  TransactionScopeAsyncFlowOption.Enabled))
{
  // connection
  using (var connection = new SqlConnection(_connectionString))
  {
    // open connection asynchronously
    await connection.OpenAsync();

    using (var command = connection.CreateCommand())
    {
      command.CommandText = ...;

      // run command asynchronously
      using (var dataReader = await command.ExecuteReaderAsync())
      {
        while (dataReader.Read())
        {
          ...
        }
      }
    }
  }
  scope.Complete();
}

10

答えに少し遅れましたが、MVC4で同じ問題が発生し、プロジェクトを右クリックしてプロパティに移動することにより、プロジェクトを4.5から4.5.1に更新しました。アプリケーションタブを選択し、ターゲットフレームワークを4.5.1に変更して、トランザクションを次のように使用します。

using (AccountServiceClient client = new AccountServiceClient())
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
}


6

Transaction.DependentClone()メソッドによって作成されたDependentTransactionを使用できます。

static void Main(string[] args)
{
  // ...

  for (int i = 0; i < 10; i++)
  {

    var dtx = Transaction.Current.DependentClone(
        DependentCloneOption.BlockCommitUntilComplete);

    tasks[i] = TestStuff(dtx);
  }

  //...
}


static async Task TestStuff(DependentTransaction dtx)
{
    using (var ts = new TransactionScope(dtx))
    {
        // do transactional stuff

        ts.Complete();
    }
    dtx.Complete();
}

DependentTransactionによる同時実行性の管理

http://adamprescott.net/2012/10/04/transactionscope-in​​-multi-threaded-applications/


2
Adam Prescottのサンプルの子タスクは非同期とマークされていませんでした。「トランザクションを実行する」をawait Task.Delay(500)このパターンのようなものに置き換えるとTransactionScope nested incorrectly、子タスクが正常に完了する前に最も外側のTransactionScope(上記の例には表示されていません)がスコープを終了するため、失敗します。に置き換えるawaitTask.Wait()機能しますが、のメリットが失われますasync
mdisibio

これは問題を解決するためのより難しい方法です。TransactionScopeは、そのすべての配管を非表示にすることです。
Eniola
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.