Saya sedang mengerjakan beberapa hal API Web menggunakan Entity Framework 6 dan salah satu metode pengontrol saya adalah "Dapatkan Semua" yang mengharapkan untuk menerima konten tabel dari database saya sebagai IQueryable<Entity>
. Di repositori saya, saya bertanya-tanya apakah ada alasan menguntungkan untuk melakukan ini secara asinkron karena saya baru menggunakan EF dengan async.
Pada dasarnya itu intinya
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
vs.
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
Akankah versi asinkron benar-benar menghasilkan manfaat kinerja di sini atau apakah saya menimbulkan biaya tambahan yang tidak perlu dengan memproyeksikan ke Daftar terlebih dahulu (menggunakan pikiran asinkron Anda) dan KEMUDIAN pergi ke IQuerable?
c#
entity-framework
async-await
Jesse Carter
sumber
sumber
Jawaban:
Masalahnya adalah Anda salah paham tentang cara kerja async / await dengan Entity Framework.
Tentang Entity Framework
Jadi, mari kita lihat kode ini:
dan contoh penggunaannya:
Apa yang terjadi disana?
IQueryable
objek (belum mengakses database) menggunakanrepo.GetAllUrls()
IQueryable
objek baru dengan kondisi tertentu menggunakan.Where(u => <condition>
IQueryable
objek baru dengan batas halaman yang ditentukan menggunakan.Take(10)
.ToList()
.IQueryable
Objek kami dikompilasi menjadi sql (likeselect 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:
Dengan contoh penggunaan yang sama yang kami dapatkan:
await context.Urls.ToListAsync();
.Tentang async / await
Mengapa async / await lebih disukai digunakan? Mari kita lihat kode ini:
Apa yang terjadi di sini?
var stuff1 = ...
userId
var stuff2 = ...
userId
Jadi mari kita lihat versi asinkronnya:
Apa yang terjadi di sini?
Cara yang tepat untuk melakukannya
Kode yang sangat bagus di sini:
Catatan, Anda harus menambahkan
using System.Data.Entity
untuk menggunakan metodeToListAsync()
IQuerable.Perhatikan, bahwa jika Anda tidak memerlukan pemfilteran dan paging dan sebagainya, Anda tidak perlu mengerjakannya
IQueryable
. Anda bisa menggunakanawait context.Urls.ToListAsync()
dan bekerja dengan terwujudList<Url>
.sumber
GetAllUrlsByUser
metode ini, Anda tidak perlu menjadikannya asinkron. Cukup kembalikan Task dan simpan diri Anda sebagai mesin status yang tidak perlu agar tidak dibuat oleh compiler.async
danawait
jika Anda TIDAK melakukan apa pun dengan daftar tersebut. Biarkan penelepon untukawait
itu. Saat Anda menunggu panggilan pada tahap ini,return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
Anda membuat pembungkus async tambahan saat Anda mendekompilasi assembly dan melihat IL.Ada perbedaan besar dalam contoh yang Anda posting, versi pertama:
Ini buruk , pada dasarnya tidak
select * from table
, mengembalikan semua hasil ke dalam memori dan kemudian menerapkannyawhere
dalam pengumpulan memori daripada melakukanselect * from table where...
terhadap database.Metode kedua tidak akan benar-benar masuk ke database hingga kueri diterapkan ke
IQueryable
(mungkin melalui.Where().Select()
operasi gaya linq yang hanya akan mengembalikan nilai db yang cocok dengan kueri.Jika contoh Anda sebanding,
async
versinya biasanya akan sedikit lebih lambat per permintaan karena ada lebih banyak overhead di mesin status yang dihasilkan kompilator untuk memungkinkanasync
fungsionalitas tersebut.Namun perbedaan utama (dan manfaat) adalah bahwa
async
versi ini memungkinkan lebih banyak permintaan bersamaan karena tidak memblokir utas pemrosesan sementara itu menunggu IO untuk menyelesaikan (kueri db, akses file, permintaan web, dll).sumber
Singkat cerita,
IQueryable
dirancang untuk menunda proses RUN dan pertama membangun ekspresi dalam hubungannya denganIQueryable
ekspresi lain , lalu menafsirkan dan menjalankan ekspresi secara keseluruhan.Tetapi
ToList()
metode (atau beberapa jenis metode seperti itu), diperlukan untuk menjalankan ekspresi secara instan "sebagaimana adanya".Metode pertama Anda (
GetAllUrlsAsync
), akan segera berjalan, karenaIQueryable
diikuti olehToListAsync()
metode. maka itu berjalan secara instan (asynchronous), dan mengembalikan banyakIEnumerable
s.Sementara metode kedua Anda (
GetAllUrls
), tidak akan dijalankan. Sebaliknya, ia mengembalikan ekspresi dan PENELI metode ini bertanggung jawab untuk menjalankan ekspresi.sumber