私は頻繁にasync/awaitを使用して、ASP.NET MVC Web APIスレッドが長時間実行されるI/Oおよびネットワーク操作、特にデータベース呼び出しによってブロックされないようにします。
System.Data.Entity名前空間は、FirstOrDefaultAsync、ContainsAsync、CountAsyncなどのさまざまなヘルパー拡張機能を提供します。などなど。
ただし、データコンテキストはスレッドセーフではないため、次のコードに問題があることを意味します。
_var dbContext = new DbContext();
var something = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
var morething = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 2);
_
実際、私は時々のような例外を見ています:
System.InvalidOperationException:接続は閉じられませんでした。接続の現在の状態は開いています。
正しいパターンは、データベースへのeach非同期呼び出しに別のusing(new DbContext...)
ブロックを使用するのですか?その代わりに同期を実行する方が潜在的に有益ですか?
ここには膠着状態があります。 ASP.NET Web API実行環境のスレッドモデルを担当するAspNetSynchronizationContext
は、await
の後の非同期継続が同じスレッドで行われることを保証しません。これの全体的なアイデアは、ASP.NETアプリのスケーラビリティを高めることであり、ThreadPool
からのスレッドがブロックされて、保留中の同期操作がブロックされます。
ただし、 DataContext
クラス(LINQ to SQLの一部) はスレッドセーフではないため、DataContext
APIでスレッドの切り替えが発生する可能性がある場所では使用しないでください。呼び出します。非同期呼び出しごとに個別のusing
構成体は、notヘルプのいずれかです。
_var something;
using (var dataContext = new DataContext())
{
something = await dataContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
}
_
これは、_DataContext.Dispose
_がオブジェクトが最初に作成されたスレッドとは異なるスレッドで実行される可能性があり、これがDataContext
が期待するものではないためです。
DataContext
APIに固執したい場合、synchronouslyを呼び出すことが唯一の実行可能なオプションのようです。そのステートメントをEF API全体に拡張する必要があるかどうかはわかりませんが、DataContext
APIで作成された子オブジェクトもおそらくスレッドセーフではないと思います。したがって、ASP.NETでは、using
スコープは、2つの隣接するawait
コール間のスコープに制限する必要があります。
await Task.Run(() => { /* do DataContext stuff here */ })
を使用して、多数の同期DataContext
呼び出しを別のスレッドにオフロードしたくなるかもしれません。ただし、それは 既知のアンチパターン になります。特に、要求を満たすために必要なスレッドの数を減らすことはないため、パフォーマンスとスケーラビリティのみを損なう可能性があるASP.NETのコンテキストでは。
残念ながら、ASP.NETの非同期アーキテクチャは優れていますが、一部の確立されたAPIやパターンとの互換性はありません(例: 同様のケース )。ここでは同時APIアクセスを扱っていないので、特に悲しいことです。つまり、複数のスレッドが同時にDataContext
オブジェクトにアクセスしようとしないからです。
マイクロソフトがフレームワークの将来のバージョンでそれに対処することを願っています。
[UPDATE]しかし、大規模な場合、EFロジックを別のプロセス(WCFサービスとして実行)にオフロードして、 ASP.NETクライアントロジックに対するスレッドセーフな非同期API。このようなプロセスは、Node.jsと同様に、イベントマシンとしてカスタム同期コンテキストを使用して調整できます。 Node.jsのようなアパートメントのプールを実行することもでき、各アパートメントはEFオブジェクトに対するスレッドアフィニティを維持します。これにより、非同期EF APIの恩恵を引き続き受けることができます。
[UPDATE]これは、この問題の解決策を見つけるための 何らかの試み です。
DataContext
class は、LINQ to SQLの一部です。 async
/await
AFAIKを理解しないため、Entity Frameworkのasync
拡張メソッドと共に使用しないでください。
DbContext
class は、EF6以上を使用している限り、async
で正常に機能します。ただし、一度に実行されるDbContext
インスタンスごとに1つの操作(同期または非同期)のみを使用できます。コードで実際にDbContext
を使用している場合は、例外の呼び出しスタックを調べて、同時使用を確認します(例:Task.WhenAll
)。
すべてのアクセスがシーケンシャルであることが確実な場合は、最小限の再現を投稿するか、Microsoft Connectにバグとして報告してください。