Sqliteを使用してEntityFrameworkCoreを実行しているDotnetCoreプロジェクトにWebApiコントローラーがあります。
アクション内のこのコードは、時折エラーを生成します。
var t1 = _dbContext.Awesome.FirstOrDefaultAsync(a => [...]);
var t2 = _dbContext.Bazinga.FirstOrDefaultAsync(b => [...]);
var r1 = await t1;
var r2 = await t2;
エラーは次のとおりです。
Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory:Error:クエリの結果の反復中にデータベースで例外が発生しました。 System.ObjectDisposedException:セーフハンドルが閉じられました
Microsoft.EntityFrameworkCore.Query.RelationalQueryCompilationContextFactory:Error:クエリの結果の反復中にデータベースで例外が発生しました。 System.InvalidOperationException:ExecuteReaderは、接続が開いているときにのみ呼び出すことができます。
私にとっての両方のエラーは、(DbContext
自体ではありませんが)時期尚早の処分のように、DbContext
で何かが起こっていることを示唆しています。 DbContext
は、Dotnet Coreの「通常の方法」の配管を使用して、コントローラーコンストラクターに挿入されます。以下に示す構成(私のStartup.cs
ConfigureServices
メソッド本体から):
services.AddDbContext<ApplicationContext>(options => options.UseSqlite(connectionString));
上記のエラー生成コードを次のようなものに変更した場合:
var r1 = await _dbContext.Awesome.FirstOrDefaultAsync(a => [...]);
var r2 = await _dbContext.Bazinga.FirstOrDefaultAsync(b => [...]);
...上記のエラーは確認していません。したがって、DbContext
(上記のように挿入)の同じインスタンスで複数のタスクを同時に実行すると、問題が発生する原因になります。明らかに、それは予想外のものです。
質問:
DbContext
で並行タスクを実行しながら、問題を回避する簡単な方法を知っていますか?残念ながら、それはできません。
EF Coreは、同じコンテキストインスタンスで実行される複数の並列操作をサポートしていません。次の操作を開始する前に、常に操作が完了するのを待つ必要があります。これは通常、各非同期操作でawaitキーワードを使用して実行されます。
EF 6ドキュメント からも
スレッドセーフ
スレッドセーフは非同期をより便利にしますが、それは直交機能です。 EFがユーザーコードで構成されるグラフと対話して状態を維持し、このコードもスレッドセーフであることを確認する簡単な方法がないことを考えると、最も一般的なケースでサポートを実装できるかどうかは不明です。
今のところ、EFは、開発者が一度に2つの非同期操作を実行してスローしようとしたかどうかを検出します。
DbContext
は、任意の時点で1つのオープンデータリーダーのみをサポートします。複数のデータベースクエリを同時に実行する場合は、同時クエリごとに1つずつ、複数のDbContext
インスタンスが必要になります。
エラーが時々発生する理由については、その競合状態です。 2つのタスクを次々に開始したからといって(待機せずに)、データベースが同時にヒットすることは保証されません。実行時間がたまたま並んでいる場合もあれば、一方のタスクが開始したときに一方のタスクがすぐに終了して競合が発生しない場合もあります。
それを回避する方法-サポートされていないのでそれをしないでください。各DbContext呼び出しを待つか、複数のDbContextインスタンスを使用します。
クエリとは、SELECT、UPDATE、DELETE、INSERT、ALTER、STORED PROCEDURE CALL、ETCを含むDB操作を意味します。