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
IQueryableobjek (belum mengakses database) menggunakanrepo.GetAllUrls()
- Kami membuat
IQueryableobjek baru dengan kondisi tertentu menggunakan.Where(u => <condition>
- Kami membuat
IQueryableobjek baru dengan batas halaman yang ditentukan menggunakan.Take(10)
- Kami mengambil hasil dari database menggunakan
.ToList(). IQueryableObjek 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.Entityuntuk 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>.