web-dev-qa-db-ja.com

.NET 4.0のMicrosoft.Bcl.Asyncを使用してTransactionScopeで非同期メソッドをサポートするにはどうすればよいですか?

私は次のような方法があります:

public async Task SaveItemsAsync(IEnumerable<MyItem> items)
{
    using (var ts = new TransactionScope())
    {
        foreach (var item in items)
        {
            await _repository.SaveItemAsync(item);
        }

        await _repository.DoSomethingElse();

        ts.Complete();
    }
}

もちろん、これには問題があります。TransactionScopeはasync/awaitでNiceを再生しないからです。

InvalidOperationExceptionで失敗し、次のメッセージが表示されます。

「TransactionScopeは、作成されたのと同じスレッドに配置する必要があります。」

TransactionScopeAsyncFlowOption in this answer について読みましたが、これはまさに私が必要としているもののようです。

ただし、この特定のプロジェクトでは、.Net 4.0をサポートするという厳しい要件があり、4.5または4.5.1にアップグレードすることはできません。したがって、私のプロジェクトでのasync/awaitの動作は、 Microsoft.Bcl.Async NuGet Package によって提供されます。

これや他の OOBパッケージTransactionScopeAsyncFlowOptionが見つからないようです。私はどこかでそれを逃していますか?

それが利用できない場合、同じ結果を達成するための代替手段はありますか?つまり、継続でスレッドを交差させても、トランザクションスコープを適切に完了するかロールバックする必要があります。

上記の例でDoSomethingElseを追加して、トランザクションスコープ内で複数の呼び出しを行う可能性があることを示しました。したがって、1回の呼び出しですべてのアイテムをデータベースに渡すだけでは実行可能なオプションではありません。

重要な場合、リポジトリは直接ADO.Net(SqlConnectionSqlCommandなど)を使用してSQLServerに書き込みます。

UPDATE 1

.Net 4.5.1からSystem.Transactions.dllを取得し、それをプロジェクトに含めることを含むソリューションがあると思いました。ただし、4.5.1がすでにインストールされているため、これは開発ボックスでのみ機能することがわかりました。 .Net4.0のみのマシンにデプロイすると機能しませんでした。 MissingMethodExceptionを与えただけです。 .Net4.0のインストールで機能するソリューションを探しています。

UPDATE 2

私はもともと2014年7月にこの質問をしました。NETFramework4.0、4.5、および4.5.1は2016年1月に サポート終了 に達しました。したがって、この質問は適用されなくなり、ここでは過去の参照のみを目的としています。

22

TransactionScopeAsyncFlowOption.Enabledを使用する必要があります

public static TransactionScope CreateAsyncTransactionScope(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
    {
        var transactionOptions = new TransactionOptions
        {
            IsolationLevel = isolationLevel,
            Timeout = TransactionManager.MaximumTimeout
        };
        return new TransactionScope(TransactionScopeOption.Required, transactionOptions, TransactionScopeAsyncFlowOption.Enabled);
    }
26

TransactionScopeは、非同期/待機操作の破棄に関してフレームワーク4.5.1で修正されました。 4.5では使用しないでください!!!

代わりに、EF6とDbContextTransactionを使用してください。

using (Entities entities = new Entities())
    using (DbContextTransaction scope = entities.Database.BeginTransaction())
    {
        entities.Database.ExecuteSqlCommand("SELECT TOP 1 KeyColumn FROM MyTable)");
        scope.Commit();
    }

詳細

TransactionScopeとAsync/Await。流れと一体に! 2015年8月6日にDanielMarbachによって書かれました。これはご存じないかもしれませんが、.NET Frameworkの4.5.0バージョンには、System.Transactions.TransactionScopeとasync/awaitでの動作に関する重大なバグが含まれています。このバグのため、TransactionScopeは非同期継続にフロースルーできません。これにより、トランザクションのスレッドコンテキストが変更される可能性があり、トランザクションスコープが破棄されるときに例外がスローされます。

これは、トランザクションを含む非同期コードの記述が非常にエラーを起こしやすいため、大きな問題です。

幸いなことに、Microsoftは.NET Framework 4.5.1の一部として、その「非同期継続」バグの修正をリリースしました。問題は、私たちのような開発者は、この新しい動作を取得するために明示的にオプトインする必要があるということです。それを行う方法を見てみましょう。

TL; DR

TransactionScopeとasync/awaitを一緒に使用している場合は、すぐに.NET4.5.1にアップグレードする必要があります。非同期コードをラップするTransactionScopeは、コンストラクターでTransactionScopeAsyncFlowOption.Enabledを指定する必要があります。

1
Pit J