キャンセル可能なasync/awaitでTransactionScopeを廃棄する方法は?
質問
新しいasync/await機能を使って、DBで非同期処理を行おうとしています。いくつかのリクエストは長くなることがあるので、それらをキャンセルできるようにしたいです。私が遭遇している問題は
TransactionScope
がスレッドアフィニティを持っているようで、タスクをキャンセルするときに、その
Dispose()
は間違ったスレッドで実行されてしまうようです。
具体的には
.TestTx()
を呼び出すと、次のようになります。
AggregateException
を含む
InvalidOperationException
で
task.Wait ()
:
"A TransactionScope must be disposed on the same thread that it was created."
以下はそのコードです。
public void TestTx () {
var cancellation = new CancellationTokenSource ();
var task = TestTxAsync ( cancellation.Token );
cancellation.Cancel ();
task.Wait ();
}
private async Task TestTxAsync ( CancellationToken cancellationToken ) {
using ( var scope = new TransactionScope () ) {
using ( var connection = new SqlConnection ( m_ConnectionString ) ) {
await connection.OpenAsync ( cancellationToken );
//using ( var command = new SqlCommand ( ... , connection ) ) {
// await command.ExecuteReaderAsync ();
// ...
//}
}
}
}
UPDATED: コメントアウトされた部分は、一度オープンした接続に対して非同期に行われる何かがあることを示すためのものです。
どのように解決するのですか?
問題は、私がコンソールアプリケーションでコードをプロトタイプ化していたことに起因しており、質問には反映されていませんでした。
async/awaitがコードの実行を継続する方法は
await
が存在するかどうかに依存します。
SynchronizationContext.Current
があるかどうかに依存し、コンソールアプリケーションには デフォルトでそれが無いため、継続は現在の
TaskScheduler
であり、これは
ThreadPool
であるため、それ(
は潜在的に?
)は別のスレッドで実行されます。
したがって、単純に
SynchronizationContext
を確実にする
TransactionScope
が作成されたのと同じスレッドで破棄されるようにします。WinForms と WPF アプリケーションはデフォルトでこれを備えていますが、コンソールアプリケーションではカスタムのものを使用するか、あるいは
DispatcherSynchronizationContext
を WPF から借りることができます。
この仕組みを詳しく説明している素晴らしいブログ記事を2つ紹介します。
Await、SynchronizationContext、およびコンソールアプリ
Await、SynchronizationContext、Console Apps。パート2
関連
-
[解決済み】IntPtrとは一体何なのか?
-
[解決済み] enumを列挙するには
-
[解決済み] intをenumにキャストするにはどうすればよいですか?
-
[解決済み] C#で文字列のエンコーディングを手動で指定せずに、一貫性のあるバイト表現を得るには?
-
[解決済み] forEachループでasync/awaitを使用する
-
[解決済み] DateTime型の誕生日から年齢を計算するにはどうしたらいいですか?
-
[解決済み] Microsoft Officeをインストールせずに、C#でExcel(.XLSおよび.XLSX)ファイルを作成するにはどうすればよいですか?
-
[解決済み] async」と「await」の使い方とタイミング
-
[解決済み] C#で同期メソッドから非同期メソッドを呼び出すには?
-
[解決済み] 複数のタスクにasync/awaitを使用する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] エンティティタイプ ApplicationUser は、現在のコンテキストのモデルの一部ではありません。
-
[解決済み】Ajax処理で「無効なJSONプリミティブ」と表示される件
-
[解決済み】Excel "外部テーブルが期待された形式ではありません。"
-
解決済み] Critical error detected c0000374 - C++ dll returns pointer off allocated memory to C# [解決済み] Critical error detected c0000374 - C++ dll returns pointer off allocated memory to C#.
-
[解決済み】SmtpException: トランスポート接続からデータを読み取れません:net_io_connectionclosed
-
[解決済み】ソケットのアドレス(プロトコル/ネットワークアドレス/ポート)は、通常1つしか使用できない?
-
[解決済み】なぜこのコードはInvalidOperationExceptionを投げるのですか?
-
[解決済み】インデックスが範囲外でした。コレクションパラメータname:indexのサイズより小さく、非負でなければなりません。
-
VSでscanfエラーを恒久的に解決するには、ソースファイルを作成し、自動的に#define _CRT_SECURE_NO_WARNINGS 1を追加してください。
-
[解決済み】WebResource.axdとは何ですか?