Jawaban:
Saya tahu orang lain telah menulis mengapa Anda menggunakan satu atau yang lain, tapi saya pikir saya akan menggambarkan mengapa Anda TIDAK boleh menggunakan satu, ketika Anda maksud yang lain.
Catatan: Dalam kode saya, saya biasanya akan menggunakan FirstOrDefault()
dan SingleOrDefault()
tapi itu pertanyaan yang berbeda.
Ambil, misalnya, tabel yang menyimpan Customers
dalam berbagai bahasa menggunakan Kunci Komposit ( ID
, Lang
):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();
Kode di atas memperkenalkan kemungkinan kesalahan logika (sulit dilacak). Ini akan mengembalikan lebih dari satu catatan (dengan asumsi Anda memiliki catatan pelanggan dalam berbagai bahasa) tetapi itu akan selalu mengembalikan hanya yang pertama ... yang kadang-kadang bisa berfungsi ... tetapi tidak yang lain. Tidak dapat diprediksi.
Karena niat Anda adalah mengembalikan satu Customer
Penggunaan Single()
;
Berikut ini akan mengeluarkan pengecualian (yang Anda inginkan dalam kasus ini):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();
Kemudian, Anda cukup memukul diri Anda sendiri dan berkata pada diri sendiri ... OOPS! Saya lupa bidang bahasa! Berikut ini adalah versi yang benar:
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();
First()
berguna dalam skenario berikut:
DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();
Ini akan mengembalikan SATU objek, dan karena Anda menggunakan penyortiran, itu akan menjadi catatan terbaru yang dikembalikan.
Menggunakan Single()
ketika Anda merasa harus secara eksplisit selalu mengembalikan 1 catatan akan membantu Anda menghindari kesalahan logika.
customers.Where(predicate).Single()
customers.Single(predicate)
?
Single akan mengeluarkan pengecualian jika menemukan lebih dari satu catatan yang cocok dengan kriteria. Pertama akan selalu memilih catatan pertama dari daftar. Jika kueri mengembalikan hanya 1 catatan, Anda dapat menggunakannya First()
.
Keduanya akan mengeluarkan InvalidOperationException
pengecualian jika koleksinya kosong. Atau Anda bisa menggunakan SingleOrDefault()
. Ini tidak akan menghasilkan pengecualian jika daftar kosong
Tunggal()
Mengembalikan elemen spesifik tunggal dari kueri
Kapan Digunakan : Jika tepat 1 elemen diharapkan; tidak 0 atau lebih dari 1. Jika daftar kosong atau memiliki lebih dari satu elemen, itu akan mengeluarkan Pengecualian "Urutan berisi lebih dari satu elemen"
SingleOrDefault ()
Mengembalikan elemen spesifik tunggal dari kueri, atau nilai default jika tidak ada hasil yang ditemukan
Saat Digunakan : Saat 0 atau 1 elemen diharapkan. Ini akan mengeluarkan pengecualian jika daftar memiliki 2 item atau lebih.
Pertama()
Mengembalikan elemen pertama dari kueri dengan beberapa hasil.
When Use : Kapan 1 atau lebih elemen diharapkan dan Anda hanya menginginkan yang pertama. Ini akan mengeluarkan pengecualian jika daftar tidak mengandung elemen.
FirstOrDefault ()
Mengembalikan elemen pertama daftar dengan jumlah elemen apa pun, atau nilai default jika daftar kosong.
When Use : Ketika beberapa elemen diharapkan dan Anda hanya menginginkan yang pertama. Atau daftar kosong dan Anda menginginkan nilai default untuk jenis yang ditentukan, sama seperti
default(MyObjectType)
. Misalnya: jika jenis daftarlist<int>
itu akan mengembalikan angka pertama dari daftar atau 0 jika daftar kosong. Jika yalist<string>
, itu akan mengembalikan string pertama dari daftar atau nol jika daftar kosong.
First
ketika 1 atau lebih elemen diharapkan , tidak hanya "lebih dari 1", dan FirstOrDefault
dengan jumlah elemen apa pun.
Ada perbedaan semantik dan halus antara kedua metode ini.
Gunakan Single
untuk mengambil elemen pertama (dan hanya) dari urutan yang seharusnya mengandung satu elemen dan tidak lebih. Jika urutan memiliki lebih dari pada elemen permintaan Anda Single
akan menyebabkan pengecualian dilempar karena Anda menunjukkan bahwa hanya ada satu elemen.
Gunakan First
untuk mengambil elemen pertama dari urutan yang dapat berisi sejumlah elemen. Jika urutan memiliki lebih dari pada elemen permintaan Anda First
tidak akan menyebabkan pengecualian untuk dilemparkan karena Anda menunjukkan bahwa Anda hanya perlu elemen pertama dalam urutan dan tidak peduli jika lebih ada.
Jika urutan tidak mengandung elemen, kedua pemanggilan metode akan menyebabkan pengecualian karena kedua metode mengharapkan setidaknya satu elemen untuk hadir.
Jika Anda tidak secara khusus ingin pengecualian dilemparkan dalam acara tersebut bahwa ada lebih dari satu item, penggunaanFirst()
.
Keduanya efisien, ambil item pertama. First()
sedikit lebih efisien karena tidak repot memeriksa untuk melihat apakah ada item kedua.
Satu-satunya perbedaan adalah bahwa Single()
mengharapkan hanya ada satu item dalam enumerasi untuk memulai, dan akan melempar pengecualian jika ada lebih dari satu. Anda menggunakan .Single()
jika Anda secara khusus ingin pengecualian yang dilemparkan dalam kasus ini.
Jika saya ingat, Single () memeriksa apakah ada elemen lain setelah yang pertama (dan melempar pengecualian jika itu terjadi), sedangkan First () berhenti setelah mendapatkannya. Keduanya melempar pengecualian jika urutannya kosong.
Secara pribadi, saya selalu menggunakan First ().
Mengenai kinerja: Seorang rekan kerja dan saya sedang mendiskusikan kinerja Single vs First (atau SingleOrDefault vs FirstOrDefault), dan saya sedang memperdebatkan poin bahwa First (atau FirstOrDefault) akan lebih cepat dan meningkatkan kinerja (saya semua tentang membuat aplikasi kami lari lebih cepat).
Saya telah membaca beberapa posting tentang Stack Overflow yang memperdebatkan hal ini. Ada yang mengatakan ada keuntungan kinerja kecil menggunakan Pertama, bukan Tunggal. Ini karena Pertama hanya akan mengembalikan item pertama sementara Tunggal harus memindai semua hasil untuk memastikan tidak ada duplikat (yaitu: jika menemukan item di baris pertama tabel, masih akan memindai setiap baris lainnya untuk pastikan tidak ada nilai kedua yang cocok dengan kondisi yang kemudian akan menimbulkan kesalahan). Saya merasa seperti berada di tanah yang kokoh dengan "Pertama" lebih cepat dari "Lajang" jadi saya mulai membuktikannya dan mengakhiri perdebatan.
Saya menyiapkan tes di database saya dan menambahkan 1.000.000 baris ID UniqueIdentifier Asing UniqueIdentifier Info nvarchar (50) (diisi dengan deretan angka "0" hingga "999.999")
Saya memuat data dan menetapkan ID sebagai bidang kunci utama.
Menggunakan LinqPad, tujuan saya adalah untuk menunjukkan bahwa jika Anda mencari nilai pada 'Asing' atau 'Info' menggunakan Tunggal, itu akan jauh lebih buruk daripada menggunakan Pertama.
Saya tidak bisa menjelaskan hasil yang saya dapatkan. Di hampir setiap kasus, menggunakan Single atau SingleOrDefault sedikit lebih cepat. Ini tidak masuk akal bagi saya, tetapi saya ingin membagikannya.
Contoh: Saya menggunakan pertanyaan berikut:
var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)
Saya mencoba pertanyaan serupa pada bidang kunci 'Asing' yang tidak diindeks berpikir yang akan membuktikan bahwa Pertama lebih cepat, tetapi Tunggal selalu sedikit lebih cepat dalam pengujian saya.
Mereka berbeda. Keduanya menyatakan bahwa set hasil tidak kosong, tetapi tunggal juga menyatakan bahwa tidak ada lebih dari 1 hasil. Saya pribadi menggunakan Tunggal dalam kasus di mana saya hanya berharap ada 1 hasil hanya karena mendapatkan lebih dari 1 hasil kembali adalah kesalahan dan mungkin harus diperlakukan seperti itu.
Anda dapat mencoba contoh sederhana untuk mendapatkan perbedaan. Pengecualian akan dilakukan pada saluran 3;
List<int> records = new List<int>{1,1,3,4,5,6};
var record = records.First(x => x == 1);
record = records.Single(x => x == 1);
Catatan dalam entitas Karyawan:
Employeeid = 1
: Hanya satu karyawan dengan ID ini
Firstname = Robert
: Lebih dari satu karyawan dengan nama ini
Employeeid = 10
: Tidak ada karyawan dengan ID ini
Sekarang perlu untuk memahami apa Single()
dan First()
artinya secara rinci.
Tunggal()
Tunggal () digunakan untuk mengembalikan catatan tunggal yang secara unik ada dalam tabel, sehingga permintaan di bawah ini akan mengembalikan Karyawan yang employeed =1
karena kita hanya memiliki satu Karyawan yang Employeed
1. Jika kita memiliki dua catatan untuk EmployeeId = 1
maka itu menimbulkan kesalahan (lihat kesalahan di bawah dalam kueri kedua tempat kami menggunakan contoh untuk Firstname
.
Employee.Single(e => e.Employeeid == 1)
Di atas akan mengembalikan satu catatan, yang memiliki 1 employeeId
Employee.Single(e => e.Firstname == "Robert")
Di atas akan melempar pengecualian karena catatan multilple ada di tabel untuk FirstName='Robert'
. Pengecualiannya adalah
InvalidOperationException: Sequence berisi lebih dari satu elemen
Employee.Single(e => e.Employeeid == 10)
Ini akan, sekali lagi, melemparkan pengecualian karena tidak ada catatan untuk id = 10. Pengecualiannya adalah
InvalidOperationException: Urutan tidak mengandung elemen.
Untuk EmployeeId = 10
itu akan mengembalikan nol, tetapi seperti yang kita gunakan Single()
akan menimbulkan kesalahan. Untuk menangani kesalahan nol, kita harus menggunakan SingleOrDefault()
.
Pertama()
Pertama () mengembalikan dari beberapa catatan, catatan yang sesuai diurutkan dalam urutan menurut birthdate
sehingga akan mengembalikan 'Robert' yang tertua.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")
Di atas harus mengembalikan yang tertua, Robert sesuai DOB.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)
Di atas akan membuang pengecualian karena tidak ada catatan untuk id = 10 ada. Untuk menghindari pengecualian nol, kita harus menggunakan FirstOrDefault()
daripada First()
.
Catatan: Kami hanya dapat menggunakan First()
/ Single()
ketika kami benar-benar yakin bahwa itu tidak dapat mengembalikan nilai nol.
Dalam kedua fungsi gunakan SingleOrDefault () ATAU FirstOrDefault () yang akan menangani pengecualian nol, dalam kasus tidak ada catatan ditemukan itu akan mengembalikan nol.