問題は、非同期/待機がEntity Frameworkでどのように機能するかを誤解しているようです。
Entity Frameworkについて
だから、このコードを見てみましょう:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
そしてその使用例:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
そこで何が起こりますか?
- を使用して
IQueryable
オブジェクトを取得しています(まだデータベースにアクセスしていません)。repo.GetAllUrls()
IQueryable
指定した条件で新しいオブジェクトを作成します.Where(u => <condition>
IQueryable
指定したページング制限で新しいオブジェクトを作成します.Take(10)
- を使用してデータベースから結果を取得します
.ToList()
。私たちのIQueryable
オブジェクトは、SQL(のようにコンパイルされますselect top 10 * from Urls where <condition>
)。データベースはインデックスを使用でき、SQLサーバーはデータベースから10個のオブジェクトのみを送信します(データベースに保存されているすべての10億のURLではありません)
さて、最初のコードを見てみましょう:
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
私たちが得たのと同じ使用例で:
- を使用して、データベースに保存されているすべての10億のURLをメモリにロードしています
await context.Urls.ToListAsync();
。
- メモリがオーバーフローしました。サーバーを強制終了する正しい方法
async / awaitについて
なぜasync / awaitの使用が推奨されるのですか?このコードを見てみましょう:
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
そこで何が起こるの?
- 1行目から
var stuff1 = ...
- 何かを取得したいというリクエストをSQLサーバーに送信します1
userId
- 待機中(現在のスレッドはブロックされています)
- 待機中(現在のスレッドはブロックされています)
- .....
- SQLサーバーが応答を送信する
- 2行目に移動します
var stuff2 = ...
- 何かを取得したいというリクエストをSQLサーバーに送信します
userId
- 待機中(現在のスレッドはブロックされています)
- そしてまた
- .....
- SQLサーバーが応答を送信する
- ビューをレンダリングします
それでは、非同期バージョンを見てみましょう。
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
そこで何が起こるの?
- SQLサーバーにリクエストを送信してstuff1を取得します(1行目)
- SQLサーバーにリクエストを送信してstuff2を取得します(2行目)
- SQLサーバーからの応答を待ちますが、現在のスレッドはブロックされず、別のユーザーからのクエリを処理できます
- ビューをレンダリングします
それを行う正しい方法
ここでとても良いコード:
using System.Data.Entity;
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
public async Task<List<URL>> GetAllUrlsByUser(int userId) {
return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
}
IQueryableのusing System.Data.Entity
メソッドを使用するために追加する必要があることに注意してくださいToListAsync()
。
フィルタリングやページングなどが必要ない場合は、で作業する必要がないことに注意してくださいIQueryable
。await context.Urls.ToListAsync()
マテリアライズドを使用して作業できList<Url>
ます。