非同期の概要

非同期操作では、クエリがデータベースで実行されている間の、スレッドのブロックを回避します。 非同期操作は、リッチ クライアント アプリケーションで応答性の高い UI を維持するために重要です。また、Web アプリケーションの他の要求を処理するためにスレッドを解放する場合に Web アプリケーションのスループットを向上させることができます。

.NET 標準に従い、EF Core では I/O を実行するすべての同期メソッドに対応する非同期メソッドが提供されます。 これらは同期メソッドと同じ効果を持ち、C# の asyncawait のキーワードで使用できます。 たとえば、データベース I/O の実行中にスレッドをブロックする DbContext.SaveChanges を使用する代わりに、DbContext.SaveChangesAsync を使用できます。

var blog = new Blog { Url = "http://sample.com" };
context.Blogs.Add(blog);
await context.SaveChangesAsync();

詳細については、一般的な C# の非同期プログラミングのドキュメントを参照してください。

警告

EF Core では、同じコンテキスト インスタンスで実行される複数の並列操作はサポートされていません。 次の操作を開始する前に、操作が完了するまで常に待機する必要があります。 これは通常、各非同期操作で await キーワードを使用することで行われます。

警告

Microsoft.Data.SqlClient の非同期実装には、残念ながらいくつかの既知の問題があります (例: #593#601、その他)。 予期しないパフォーマンスの問題が発生する場合、特に大きなテキストまたはバイナリ値を処理する場合は、代わりに同期コマンドの実行を使用してみてください。

Note

EF Core では、基になる使用中のデータベース プロバイダー (Microsoft.Data.SqlClient など) にキャンセル トークンを渡します。 これらのトークンは、受け入れ可能な場合とそうでない場合があります。データベース プロバイダーのドキュメントを参照してください。

非同期 LINQ 演算子

LINQ クエリの非同期実行をサポートするために、EF Core ではクエリを実行して結果を返す非同期拡張メソッドのセットを提供します。 標準の同期 LINQ 演算子に対応するこれらの演算子には、ToListAsyncSingleAsyncAsAsyncEnumerable などが含まれます。

var blogs = await context.Blogs.Where(b => b.Rating > 3).ToListAsync();

ただし、WhereOrderBy などの一部の LINQ 演算子には非同期バージョンはありません。これらはでは LINQ 式のツリーのみがビルドされ、データベースでクエリが実行されないためです。 クエリの実行の原因となる演算子のみ、対応する非同期演算子があります。

重要

EF Core の非同期拡張メソッドは、Microsoft.EntityFrameworkCore 名前空間で定義されています。 メソッドを使用可能にするには、この名前空間がインポートされている必要があります。

クライアント側の非同期 LINQ 演算子

上記で説明した非同期 LINQ 演算子は EF クエリでのみ使用できます。クライアント側の LINQ to Objects クエリでは使用できません。 EF の外部でクライアント側の非同期 LINQ 操作を実行するには、System.Linq.Async パッケージを使用します。このパッケージは、サーバーで評価するために変換できない操作をクライアント側で実行する場合に特に役立ちます。

EF Core 6.0 以下では、System.Linq.Async を参照すると、残念ながら LINQ 演算子のあいまいな呼び出しコンパイル エラーが EF の DbSet に適用されます。これにより、EF と System.Linq.Async を同じプロジェクトで使用するのが難しくなります。 この問題を回避するには、AsQueryable を DbSet に追加します。

var groupedHighlyRatedBlogs = await context.Blogs
    .AsQueryable()
    .Where(b => b.Rating > 3) // server-evaluated
    .AsAsyncEnumerable()
    .GroupBy(b => b.Rating) // client-evaluated
    .ToListAsync();