Saya telah mencari perbedaan antara 2 pasang di atas tetapi belum menemukan artikel yang menjelaskan dengan jelas tentang hal itu serta kapan harus menggunakan satu atau lainnya.
Jadi apa perbedaan antara SaveChanges()
dan SaveChangesAsync()
?
Dan antara Find()
dan FindAsync()
?
Di sisi server, saat kami menggunakan Async
metode, kami juga perlu menambahkan await
. Jadi, menurut saya ini tidak asinkron di sisi server.
Apakah ini hanya membantu mencegah pemblokiran UI di browser sisi klien? Ataukah ada pro dan kontra di antara keduanya?
c#
entity-framework
async-await
Hien Tran
sumber
sumber
Jawaban:
Kapan pun Anda perlu melakukan tindakan di server jarak jauh, program Anda membuat permintaan, mengirimkannya, lalu menunggu tanggapan. Saya akan menggunakan
SaveChanges()
danSaveChangesAsync()
sebagai contoh tetapi hal yang sama berlaku untukFind()
danFindAsync()
.Katakanlah Anda memiliki daftar
myList
100+ item yang perlu Anda tambahkan ke database Anda. Untuk memasukkannya, fungsi Anda akan terlihat seperti ini:using(var context = new MyEDM()) { context.MyTable.AddRange(myList); context.SaveChanges(); }
Pertama Anda membuat instance
MyEDM
, menambahkan daftarmyList
ke tabelMyTable
, lalu memanggilSaveChanges()
untuk mempertahankan perubahan ke database. Ini berfungsi seperti yang Anda inginkan, record dikomit, tetapi program Anda tidak dapat melakukan apa pun sampai komit selesai. Ini bisa memakan waktu lama tergantung pada apa yang Anda lakukan. Jika Anda melakukan perubahan pada record, entitas harus mengkomitnya satu per satu (saya pernah menyimpan membutuhkan waktu 2 menit untuk update)!Untuk mengatasi masalah ini, Anda dapat melakukan salah satu dari dua hal. Yang pertama adalah Anda dapat memulai utas baru untuk menangani penyisipan. Meskipun ini akan membebaskan utas panggilan untuk terus mengeksekusi, Anda membuat utas baru yang hanya akan duduk di sana dan menunggu. Tidak perlu biaya tambahan itu, dan inilah yang
async await
dipecahkan oleh polanya.Untuk pengoperasian I / O,
await
cepatlah menjadi sahabat Anda. Mengambil bagian kode dari atas, kita dapat memodifikasinya menjadi:using(var context = new MyEDM()) { Console.WriteLine("Save Starting"); context.MyTable.AddRange(myList); await context.SaveChangesAsync(); Console.WriteLine("Save Complete"); }
Ini adalah perubahan yang sangat kecil, tetapi ada efek yang sangat besar pada efisiensi dan kinerja kode Anda. Lalu apa yang terjadi? Awal kodenya sama, Anda membuat instance
MyEDM
dan menambahkanmyList
keMyTable
. Tetapi ketika Anda memanggilawait context.SaveChangesAsync()
, eksekusi kode kembali ke fungsi panggilan! Jadi, saat Anda menunggu semua record tersebut dijalankan, kode Anda dapat terus dieksekusi. Katakanlah fungsi yang berisi kode di atas memiliki tanda tanganpublic async Task SaveRecords(List<MyTable> saveList)
, fungsi pemanggilan bisa terlihat seperti ini:public async Task MyCallingFunction() { Console.WriteLine("Function Starting"); Task saveTask = SaveRecords(GenerateNewRecords()); for(int i = 0; i < 1000; i++){ Console.WriteLine("Continuing to execute!"); } await saveTask; Console.Log("Function Complete"); }
Mengapa Anda memiliki fungsi seperti ini, saya tidak tahu, tetapi apa yang dihasilkannya menunjukkan cara
async await
kerjanya. Pertama, mari kita bahas apa yang terjadi.Eksekusi masuk
MyCallingFunction
,Function Starting
laluSave Starting
ditulis ke konsol, lalu fungsiSaveChangesAsync()
dipanggil. Pada titik ini, eksekusi kembali keMyCallingFunction
dan memasuki penulisan loop for 'Continuing to Execute' hingga 1000 kali. SetelahSaveChangesAsync()
selesai, eksekusi kembali keSaveRecords
fungsi, menulisSave Complete
ke konsol. Setelah semuanyaSaveRecords
selesai, eksekusi akan dilanjutkanMyCallingFunction
jika sudahSaveChangesAsync()
selesai. Bingung? Berikut adalah contoh keluarannya:Atau mungkin:
Itulah keindahannya
async await
, kode Anda dapat terus berjalan saat Anda menunggu sesuatu untuk diselesaikan. Pada kenyataannya, Anda akan memiliki fungsi yang lebih seperti ini sebagai fungsi panggilan Anda:public async Task MyCallingFunction() { List<Task> myTasks = new List<Task>(); myTasks.Add(SaveRecords(GenerateNewRecords())); myTasks.Add(SaveRecords2(GenerateNewRecords2())); myTasks.Add(SaveRecords3(GenerateNewRecords3())); myTasks.Add(SaveRecords4(GenerateNewRecords4())); await Task.WhenAll(myTasks.ToArray()); }
Di sini, Anda memiliki empat fungsi penyimpanan catatan yang berbeda yang berjalan pada saat bersamaan .
MyCallingFunction
akan menyelesaikan penggunaan lebih cepatasync await
daripada jikaSaveRecords
fungsi individu dipanggil secara seri.Satu hal yang belum saya singgung adalah
await
kata kunci. Apa yang dilakukannya adalah menghentikan fungsi saat ini dari eksekusi hingga apa pun yangTask
Anda tunggu selesai. Jadi dalam kasus aslinyaMyCallingFunction
, barisFunction Complete
tidak akan ditulis ke konsol sampaiSaveRecords
fungsinya selesai.Singkat cerita, jika Anda memiliki opsi untuk menggunakan
async await
, Anda harus melakukannya karena akan sangat meningkatkan kinerja aplikasi Anda.sumber
await
, meskipun ANDA tidak perlu melakukan apa pun setelah panggilan ke SaveChanges, ASP akan mengatakan "aha, utas ini kembali menunggu operasi asinkron, ini berarti saya dapat membiarkan utas ini menangani beberapa permintaan lain untuk sementara waktu ! " Ini membuat skala aplikasi Anda jauh lebih baik secara horizontal.await
melakukannyaSaveChangesAsync
karena EF tidak mendukung banyak penyelamatan pada saat yang bersamaan. docs.microsoft.com/en-us/ef/core/saving/async Selain itu, sebenarnya ada keuntungan besar dalam menggunakan metode asinkron ini. Misalnya, Anda dapat terus menerima permintaan lain di webApi Anda saat menyimpan data atau melakukan banyak penanganan, atau meningkatkan pengalaman pengguna dengan tidak membekukan antarmuka saat Anda berada di aplikasi desktop.Penjelasan saya yang tersisa akan didasarkan pada potongan kode berikut.
using System; using System.Threading; using System.Threading.Tasks; using static System.Console; public static class Program { const int N = 20; static readonly object obj = new object(); static int counter; public static void Job(ConsoleColor color, int multiplier = 1) { for (long i = 0; i < N * multiplier; i++) { lock (obj) { counter++; ForegroundColor = color; Write($"{Thread.CurrentThread.ManagedThreadId}"); if (counter % N == 0) WriteLine(); ResetColor(); } Thread.Sleep(N); } } static async Task JobAsync() { // intentionally removed } public static async Task Main() { // intentionally removed } }
Kasus 1
static async Task JobAsync() { Task t = Task.Run(() => Job(ConsoleColor.Red, 1)); Job(ConsoleColor.Green, 2); await t; Job(ConsoleColor.Blue, 1); } public static async Task Main() { Task t = JobAsync(); Job(ConsoleColor.White, 1); await t; }
Keterangan: Karena bagian sinkron (hijau)
JobAsync
berputar lebih panjang dari tugast
(merah) maka tugast
sudah selesai pada titikawait t
. Akibatnya, lanjutan (biru) berjalan pada utas yang sama dengan utas hijau. Bagian sinkronMain
(putih) akan berputar setelah bagian hijau selesai berputar. Itulah mengapa bagian sinkron dalam metode asinkron bermasalah.Kasus 2
static async Task JobAsync() { Task t = Task.Run(() => Job(ConsoleColor.Red, 2)); Job(ConsoleColor.Green, 1); await t; Job(ConsoleColor.Blue, 1); } public static async Task Main() { Task t = JobAsync(); Job(ConsoleColor.White, 1); await t; }
Keterangan: Kasus ini berlawanan dengan kasus pertama. Bagian sinkron (hijau)
JobAsync
berputar lebih pendek dari tugast
(merah) maka tugast
belum selesai pada titikawait t
. Akibatnya, lanjutan (biru) berjalan pada utas yang berbeda dengan utas hijau. Bagian sinkronMain
(putih) masih berputar setelah bagian hijau selesai berputar.Kasus 3
static async Task JobAsync() { Task t = Task.Run(() => Job(ConsoleColor.Red, 1)); await t; Job(ConsoleColor.Green, 1); Job(ConsoleColor.Blue, 1); } public static async Task Main() { Task t = JobAsync(); Job(ConsoleColor.White, 1); await t; }
Catatan: Kasus ini akan menyelesaikan masalah dalam kasus sebelumnya tentang bagian sinkron dalam metode asinkron. Tugasnya
t
langsung ditunggu. Akibatnya, lanjutan (biru) berjalan pada utas yang berbeda dengan utas hijau. Bagian sinkronMain
(putih) akan langsung berputar sejajarJobAsync
.Jika Anda ingin menambahkan kasus lain, silakan edit.
sumber
Pernyataan ini salah:
Anda tidak perlu menambahkan "await",
await
ini hanyalah kata kunci yang nyaman di C # yang memungkinkan Anda untuk menulis lebih banyak baris kode setelah panggilan, dan baris lainnya hanya akan dijalankan setelah operasi Simpan selesai. Tetapi seperti yang Anda tunjukkan, Anda dapat melakukannya hanya dengan menelepon,SaveChanges
bukanSaveChangesAsync
.Namun pada dasarnya, panggilan asinkron lebih dari itu. Idenya di sini adalah bahwa jika ada pekerjaan lain yang dapat Anda lakukan (di server) saat operasi Simpan sedang berlangsung, maka Anda harus menggunakan
SaveChangesAsync
. Jangan gunakan "menunggu". Panggil sajaSaveChangesAsync
, lalu lanjutkan melakukan hal lain secara paralel. Ini termasuk kemungkinan, dalam aplikasi web, mengembalikan respons ke klien bahkan sebelum Penyimpanan selesai. Tetapi tentu saja, Anda masih ingin memeriksa hasil akhir dari Simpan sehingga jika gagal, Anda dapat mengkomunikasikannya kepada pengguna Anda, atau mencatatnya entah bagaimana.sumber