Masalahnya adalah Anda salah paham tentang cara kerja async / await dengan Entity Framework.
Tentang Entity Framework
Jadi, mari kita lihat kode ini:
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
dan contoh penggunaannya:
repo.GetAllUrls().Where(u => <condition>).Take(10).ToList()
Apa yang terjadi disana?
- Kami mendapatkan
IQueryable
objek (belum mengakses database) menggunakanrepo.GetAllUrls()
- Kami membuat
IQueryable
objek baru dengan kondisi tertentu menggunakan.Where(u => <condition>
- Kami membuat
IQueryable
objek baru dengan batas halaman yang ditentukan menggunakan.Take(10)
- Kami mengambil hasil dari database menggunakan
.ToList()
. IQueryable
Objek kami dikompilasi menjadi sql (like select top 10 * from Urls where <condition>
). Dan database dapat menggunakan indeks, server sql hanya mengirimi Anda 10 objek dari database Anda (tidak semua miliar url disimpan dalam database)
Oke, mari kita lihat kode pertama:
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
Dengan contoh penggunaan yang sama yang kami dapatkan:
- Kami sedang memuat dalam memori semua miliar url yang disimpan dalam database Anda menggunakan
await context.Urls.ToListAsync();
.
- Kami mengalami kelebihan memori. Cara yang tepat untuk mematikan server Anda
Tentang async / await
Mengapa async / await lebih disukai digunakan? Mari kita lihat kode ini:
var stuff1 = repo.GetStuff1ForUser(userId);
var stuff2 = repo.GetStuff2ForUser(userId);
return View(new Model(stuff1, stuff2));
Apa yang terjadi di sini?
- Mulai di baris 1
var stuff1 = ...
- Kami mengirim permintaan ke sql server yang kami inginkan untuk mendapatkan beberapa barang1
userId
- Kami menunggu (utas saat ini diblokir)
- Kami menunggu (utas saat ini diblokir)
- .....
- Server Sql mengirimkan tanggapan kepada kami
- Kami pindah ke baris 2
var stuff2 = ...
- Kami mengirim permintaan ke server sql yang kami inginkan untuk mendapatkan beberapa barang2
userId
- Kami menunggu (utas saat ini diblokir)
- Dan lagi
- .....
- Server Sql mengirimkan tanggapan kepada kami
- Kami memberikan tampilan
Jadi mari kita lihat versi asinkronnya:
var stuff1Task = repo.GetStuff1ForUserAsync(userId);
var stuff2Task = repo.GetStuff2ForUserAsync(userId);
await Task.WhenAll(stuff1Task, stuff2Task);
return View(new Model(stuff1Task.Result, stuff2Task.Result));
Apa yang terjadi di sini?
- Kami mengirim permintaan ke server sql untuk mendapatkan barang1 (baris 1)
- Kami mengirim permintaan ke server sql untuk mendapatkan barang2 (baris 2)
- Kami menunggu tanggapan dari server sql, tetapi utas saat ini tidak diblokir, dia dapat menangani kueri dari pengguna lain
- Kami memberikan tampilan
Cara yang tepat untuk melakukannya
Kode yang sangat bagus di sini:
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();
}
Catatan, Anda harus menambahkan using System.Data.Entity
untuk menggunakan metode ToListAsync()
IQuerable.
Perhatikan, bahwa jika Anda tidak memerlukan pemfilteran dan paging dan sebagainya, Anda tidak perlu mengerjakannya IQueryable
. Anda bisa menggunakan await context.Urls.ToListAsync()
dan bekerja dengan terwujud List<Url>
.