LINQ: Kapan harus menggunakan SingleOrDefault vs. FirstOrDefault () dengan kriteria penyaringan

506

Pertimbangkan metode ekstensi IEnumerable SingleOrDefault()danFirstOrDefault()

Dokumen MSDN itu SingleOrDefault :

Mengembalikan satu-satunya elemen urutan, atau nilai default jika urutannya kosong; Metode ini melempar pengecualian jika ada lebih dari satu elemen dalam urutan.

sedangkan FirstOrDefaultdari MSDN (mungkin saat menggunakan OrderBy()atau OrderByDescending() atau tidak sama sekali),

Mengembalikan elemen pertama dari suatu urutan

Pertimbangkan beberapa contoh kueri, tidak selalu jelas kapan harus menggunakan kedua metode ini:

var someCust = db.Customers
.SingleOrDefault(c=>c.ID == 5); //unlikely(?) to be more than one, but technically COULD BE

var bobbyCust = db.Customers
.FirstOrDefault(c=>c.FirstName == "Bobby"); //clearly could be one or many, so use First?

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or does it matter?

Pertanyaan

Konvensi apa yang Anda ikuti atau sarankan ketika memutuskan untuk menggunakan SingleOrDefault()dan FirstOrDefault()dalam pertanyaan LINQ Anda?

p.campbell
sumber

Jawaban:

466

Setiap kali Anda menggunakan SingleOrDefault, Anda dengan jelas menyatakan bahwa kueri harus menghasilkan paling banyak satu hasil. Di sisi lain, kapanFirstOrDefault digunakan, kueri dapat mengembalikan jumlah hasil berapapun tetapi Anda menyatakan bahwa Anda hanya menginginkan yang pertama.

Saya pribadi menemukan semantik yang sangat berbeda dan menggunakan yang semantik, tergantung pada hasil yang diharapkan, meningkatkan keterbacaan.

Bryan Menard
sumber
164
Perbedaan yang sangat penting adalah bahwa jika Anda menggunakan SingleOrDefault pada urutan dengan lebih dari satu elemen, itu melempar pengecualian.
Kamran Bigdely
17
@kami jika tidak melempar pengecualian, itu akan persis seperti FirstOrDefault. Pengecualian adalah apa yang membuatnya menjadi SingleOrDefault. Poin bagus mengemukakannya, dan menempatkan paku pada peti mati perbedaan.
Fabio S.
17
Saya harus mengatakan bahwa dari segi kinerja, FirstOrDefault bekerja sekitar 10 kali lebih cepat dari SingleOrDefault, menggunakan Daftar <MyClass> dari 9.000.000 elemen, kelas berisi 2 bilangan bulat dan Funcenya berisi pencarian dua bilangan bulat ini. Pencarian 200 kali dalam satu loop membutuhkan 22 detik pada var v = list.SingleOrDefault (x => x.Id1 == i && x.Id2 == i); dan var v = list.FirstOrDefault (x => x.Id1 == i && x.Id2 == i); sekitar 3 detik
Chen
6
@BitsandBytesHandyman Jika SignleOrDefault tidak melempar pengecualian ketika urutan berisi lebih dari satu item tidak akan berperilaku persis seperti FirstOrDefault. FirstOrDefault mengembalikan item pertama, atau nol jika urutannya kosong. SingleOrDefault harus mengembalikan satu-satunya item, atau nol jika urutannya kosong ATAU jika berisi lebih dari satu item, tanpa melemparkan pengecualian sama sekali.
Thanasis Ioannidis
2
@ RSW Ya, saya tahu itu. Membaca dengan cermat komentar saya, saya mengatakan apa yang harus dilakukan SingleOrDefault, bukan apa yang dilakukannya. Tapi tentu saja, apa yang harus dilakukan, sangat subjektif. Bagi saya, pola "SomethingOrDefault" berarti: Dapatkan nilai "Sesuatu". Jika "Sesuatu" gagal mengembalikan nilai, kembalikan nilai default. Yang berarti bahwa nilai default harus dikembalikan bahkan dalam kasus "Sesuatu" akan melempar pengecualian. Jadi, di mana Single akan melempar pengecualian, SingleOrDefault harus mengembalikan nilai default, dalam perspektif saya.
Thanasis Ioannidis
585

Jika set hasil Anda mengembalikan 0 catatan:

  • SingleOrDefault mengembalikan nilai default untuk tipe (mis. default untuk int adalah 0)
  • FirstOrDefault mengembalikan nilai default untuk tipe tersebut

Jika Anda menghasilkan set, kembalikan 1 catatan:

  • SingleOrDefault mengembalikan catatan itu
  • FirstOrDefault mengembalikan catatan itu

Jika set hasil Anda mengembalikan banyak catatan:

  • SingleOrDefault melempar pengecualian
  • FirstOrDefault mengembalikan catatan pertama

Kesimpulan:

Jika Anda ingin pengecualian dilemparkan jika set hasil berisi banyak catatan, gunakan SingleOrDefault.

Jika Anda selalu ingin 1 catatan apa pun yang berisi hasil set, gunakan FirstOrDefault

Alex
sumber
6
Saya menyarankan bahwa jarang benar-benar menginginkan pengecualian sehingga sebagian besar waktu FirstOrDefault lebih disukai. Saya tahu ada kasus yang tidak sering muncul.
MikeKulls
FirstOrDefaultdikembalikan catatan pertama berarti catatan baru (terakhir) / catatan lama (pertama)? bisakah Anda mengklarifikasi saya?
Duk
@ Aduh, itu tergantung pada bagaimana Anda mengurutkan catatan. Anda dapat menggunakan OrderBy () atau OrderByDescending () dll sebelum memanggil FirstOrDefault. Lihat contoh kode OP.
Gan
5
Saya juga suka jawaban ini. Terutama mengingat bahwa ada beberapa kesempatan di mana Anda benar-benar ingin pengecualian dilemparkan karena Anda berniat untuk menangani kasus langka itu di tempat lain dan bukan hanya berpura-pura itu tidak terjadi. Ketika Anda ingin pengecualian, Anda mengatakan ini dengan jelas, dan juga memaksa orang lain untuk menangani hanya membungkus membuat sistem keseluruhan lebih kuat.
Francis Rodgers
Ini sangat jelas dinyatakan sehingga orang dapat dengan mudah mengerti.
Nirav Vasoya
244

Ada

  • perbedaan semantik
  • perbedaan kinerja

antara keduanya.

Perbedaan semantik:

  • FirstOrDefault mengembalikan item pertama yang berpotensi banyak (atau default jika tidak ada).
  • SingleOrDefaultmengasumsikan bahwa ada satu item dan mengembalikannya (atau default jika tidak ada). Beberapa item merupakan pelanggaran kontrak, pengecualian dilemparkan.

Perbedaan Kinerja

  • FirstOrDefaultbiasanya lebih cepat, iterates sampai ia menemukan elemen dan hanya perlu mengulangi seluruh enumerable ketika tidak menemukannya. Dalam banyak kasus, ada kemungkinan besar untuk menemukan item.

  • SingleOrDefaultperlu memeriksa apakah hanya ada satu elemen dan karena itu selalu iterates seluruh enumerable. Lebih tepatnya, itu berulang sampai menemukan elemen kedua dan melempar pengecualian. Tetapi dalam kebanyakan kasus, tidak ada elemen kedua.

Kesimpulan

  • Gunakan FirstOrDefaultjika Anda tidak peduli berapa banyak item yang ada atau ketika Anda tidak mampu memeriksa keunikan (misalnya dalam koleksi yang sangat besar). Saat Anda memeriksa keunikan dalam menambahkan item ke koleksi, mungkin terlalu mahal untuk memeriksanya lagi saat mencari item tersebut.

  • Gunakan SingleOrDefaultjika Anda tidak perlu terlalu memperhatikan kinerja dan ingin memastikan bahwa asumsi satu item jelas untuk pembaca dan diperiksa saat runtime.

Dalam praktiknya, Anda menggunakan First/ FirstOrDefaultsering bahkan dalam kasus ketika Anda mengasumsikan satu item, untuk meningkatkan kinerja. Anda harus tetap ingat bahwa Single/ saya SingleOrDefaultdapat meningkatkan keterbacaan (karena itu menyatakan asumsi satu item) dan stabilitas (karena memeriksa itu) dan menggunakannya dengan tepat.

Stefan Steinegger
sumber
16
+1 "atau ketika Anda tidak mampu memeriksa keunikan (mis. Dalam koleksi yang sangat besar)." . Saya mencari ini. Saya juga akan menambahkan menegakkan keunikan saat memasukkan, atau / dengan desain alih-alih pada saat membuat kueri!
Nawaz
Saya bisa membayangkan iterate atas SingleOrDefaultbanyak objek ketika menggunakan Linq untuk Objects, tetapi tidak SingleOrDefaultharus mengulangi paling banyak 2 item jika Linq berbicara ke database misalnya? Hanya ingin tahu ..
Memet Olsen
3
@memetolsen Pertimbangkan kode meludah untuk keduanya dengan LINQ ke SQL - FirstOrDefault menggunakan Top 1. SingleOrDefault menggunakan Top 2.
Jim Wooley
@ JimWooley Kurasa aku salah mengerti kata 'enumerable'. Saya pikir Stefan yang dimaksud dengan C # Enumerable.
Memet Olsen
1
@memetolsen benar dalam hal jawaban asli, komentar Anda merujuk ke database, jadi saya menawarkan apa yang terjadi dari penyedia. Sementara kode .Net hanya mengulangi lebih dari 2 nilai, basis data mengunjungi sebanyak mungkin catatan hingga mencapai yang kedua memenuhi kriteria.
Jim Wooley
76

Tidak ada yang menyebutkan bahwa FirstOrDefault diterjemahkan dalam SQL tidak merekam TOP 1, dan SingleOrDefault melakukan TOP 2, karena perlu tahu apakah ada lebih dari 1 catatan.

shalke
sumber
3
Ketika saya menjalankan SingleOrDefault melalui LinqPad dan VS, saya tidak pernah mendapatkan SELECT TOP 2, dengan FirstOrDefault saya bisa mendapatkan SELECT TOP 1, tetapi sejauh yang saya tahu Anda tidak mendapatkan SELECT TOP 2.
Jamie R Rytlewski
Hai, saya telah mencoba di linqpad juga dan permintaan sql membuat saya takut karena itu benar-benar mengambil semua baris. Saya tidak yakin bagaimana ini bisa terjadi?
Siapa saja
1
Ini sepenuhnya tergantung pada penyedia LINQ yang digunakan. Misalnya, LINQ ke SQL dan LINQ ke Entitas dapat menerjemahkan ke SQL dengan berbagai cara. Saya baru saja mencoba LINQPad dengan penyedia IQ MySql, dan FirstOrDefault()menambahkan LIMIT 0,1sambil SingleOrDefault()menambahkan apa-apa.
Lucas
1
EF Core 2.1 menerjemahkan FirstOrDefault ke SELECT TOP (1), SingleOrDefault ke SELECT TOP (2)
camainc
19

Untuk LINQ -> SQL:

SingleOrDefault

  • akan menghasilkan permintaan seperti "pilih * dari pengguna di mana userid = 1"
  • Pilih catatan yang cocok, Melempar pengecualian jika lebih dari satu catatan ditemukan
  • Gunakan jika Anda mengambil data berdasarkan kolom kunci utama / unik

Masalah Pertama atau Kesalahan

  • akan menghasilkan permintaan seperti "pilih top 1 * dari pengguna di mana userid = 1"
  • Pilih baris yang cocok pertama
  • Gunakan jika Anda mengambil data berdasarkan kolom kunci non primer / unik
Prakash
sumber
saya pikir Anda harus menghapus "Pilih semua baris yang cocok" dari SingleOrDefault
Saim Abdullah
10

Saya menggunakan SingleOrDefaultdalam situasi di mana logika saya menentukan bahwa akan menjadi nol atau satu hasil. Jika ada lebih banyak, ini adalah situasi kesalahan, yang sangat membantu.

pemboros
sumber
3
Seringkali saya menemukan bahwa SingleOrDefault () menyoroti kasus-kasus di mana saya belum menerapkan pemfilteran yang benar pada set hasil, atau di mana ada masalah dengan duplikasi dalam data yang mendasarinya. Lebih sering daripada tidak saya menemukan diri saya menggunakan metode Single () dan SingleOrDefault () di atas metode First ().
TimS
Ada implikasi kinerja untuk Single () dan SingleOrDefault () pada LINQ ke Objects jika Anda memiliki enumerable yang besar tetapi ketika berbicara dengan database (misalnya SQL Server) itu akan melakukan panggilan 2 besar dan jika indeks Anda diatur dengan benar bahwa panggilan tidak harus mahal dan saya lebih baik gagal cepat dan menemukan masalah data daripada mungkin memperkenalkan masalah data lainnya dengan mengambil duplikat yang salah saat memanggil Pertama () atau FirstOrDefault ().
heartlandcoder
5

SingleOrDefault: Anda mengatakan bahwa "Paling banyak" ada satu item yang cocok dengan kueri atau default FirstOrDefault: Anda mengatakan bahwa ada "Setidaknya" satu item yang cocok dengan kueri atau default

Katakan dengan keras waktu berikutnya Anda harus memilih dan Anda akan memilih dengan bijak. :)

Steven
sumber
5
Sebenarnya tidak memiliki hasil adalah penggunaan FirstOrDefault. More correctly: FirstOrDefault` = yang dapat diterima sepenuhnya = Sejumlah hasil tetapi saya hanya peduli pada yang pertama, mungkin juga tidak ada hasil. SingleOrDefault= Ada 1 atau 0 hasil, jika ada lebih banyak itu berarti ada kesalahan di suatu tempat. First= Setidaknya ada satu hasil, dan saya menginginkannya. Single= Ada 1 hasil, tidak lebih, tidak kurang, dan saya ingin yang itu.
Davy8
4

Dalam kasus Anda, saya akan menggunakan yang berikut:

pilih dengan ID == 5: boleh saja menggunakan SingleOrDefault di sini, karena Anda mengharapkan satu entitas [atau tidak sama sekali], jika Anda mendapatkan lebih dari satu entitas dengan ID 5, ada sesuatu yang salah dan tentu saja pengecualian layak.

ketika mencari orang yang nama depannya sama dengan "Bobby", mungkin ada lebih dari satu (sangat mungkin saya akan berpikir), jadi Anda tidak boleh menggunakan Tunggal atau Pertama, cukup pilih dengan Operasi di mana (jika "Bobby" mengembalikan terlalu banyak entitas, pengguna harus mempersempit pencariannya atau memilih salah satu hasil yang dikembalikan)

urutan berdasarkan tanggal pembuatan juga harus dilakukan dengan Operasi di mana (tidak mungkin hanya memiliki satu entitas, pengurutan tidak akan banyak berguna;) namun ini menyiratkan Anda ingin SEMUA entitas diurutkan - jika Anda ingin SATU saja, gunakan FirstOrDefault, Single akan melempar setiap kali jika Anda mendapatkan lebih dari satu entitas.

Olli
sumber
3
Saya tidak setuju. Jika ID basis data Anda adalah kunci utama, maka basis data tersebut sudah menegakkan keunikan. Membuang-buang siklus CPU untuk memeriksa apakah database melakukan tugasnya pada setiap permintaan hanya konyol.
John Henckel
4

Keduanya adalah operator elemen dan mereka digunakan untuk memilih elemen tunggal dari suatu urutan. Tetapi ada perbedaan kecil di antara mereka. Operator SingleOrDefault () akan melempar eksepsi jika lebih dari satu elemen puas dengan kondisi di mana FirstOrDefault () tidak akan melempar eksepsi untuk hal yang sama. Inilah contohnya.

List<int> items = new List<int>() {9,10,9};
//Returns the first element of a sequence after satisfied the condition more than one elements
int result1 = items.Where(item => item == 9).FirstOrDefault();
//Throw the exception after satisfied the condition more than one elements
int result3 = items.Where(item => item == 9).SingleOrDefault();
Sheo Dayal Singh
sumber
2
"Ada perbedaan kecil di antara mereka" - Itu besar!
Nikhil Vartak
3

Dalam contoh terakhir Anda:

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or doesn't matter?

Ya itu. Jika Anda mencoba menggunakan SingleOrDefault()dan hasil kueri lebih dari catatan Anda akan mendapatkan dan pengecualian. Satu-satunya waktu Anda dapat menggunakan dengan aman SingleOrDefault()adalah ketika Anda mengharapkan hanya 1 dan hanya 1 hasil ...

bytebender
sumber
Itu benar. Jika Anda mendapatkan 0 hasil, Anda juga mendapatkan pengecualian.
Dennis Rongo
1

Jadi seperti yang saya mengerti sekarang, SingleOrDefault akan lebih baik jika Anda meminta data yang dijamin unik yaitu ditegakkan oleh batasan DB seperti kunci primer.

Atau ada cara yang lebih baik untuk menanyakan kunci utama.

Dengan asumsi TableAcc saya miliki

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

dan saya ingin meminta AccountNumber 987654, saya gunakan

var data = datacontext.TableAcc.FirstOrDefault(obj => obj.AccountNumber == 987654);
MG
sumber
1

Menurut pendapat saya FirstOrDefaultterlalu sering digunakan. Dalam sebagian besar kasus saat Anda memfilter data, Anda akan diharapkan untuk mendapatkan kembali kumpulan elemen yang cocok dengan kondisi logis atau elemen unik tunggal dengan pengidentifikasi unik - seperti pengguna, buku, posting dll ... Itu mengapa kita bahkan bisa mengatakan bahwa itu FirstOrDefault()adalah bau kode bukan karena ada sesuatu yang salah dengan itu tetapi karena terlalu sering digunakan. Posting blog ini mengeksplorasi topik secara rinci. IMO sebagian besar kali SingleOrDefault()adalah alternatif yang jauh lebih baik jadi waspadai kesalahan ini dan pastikan Anda menggunakan metode yang paling tepat yang dengan jelas mewakili kontrak dan harapan Anda.

Vasil Kosturski
sumber
-1

Satu hal yang terlewatkan dalam tanggapan ....

Jika ada beberapa hasil, FirstOrDefault tanpa perintah oleh dapat membawa kembali hasil yang berbeda berdasarkan strategi indeks yang pernah digunakan oleh server.

Secara pribadi saya tidak tahan melihat FirstOrDefault dalam kode karena bagi saya dikatakan pengembang tidak peduli dengan hasilnya. Dengan pesanan meskipun itu bisa berguna sebagai cara menegakkan yang terbaru / paling awal. Saya harus memperbaiki banyak masalah yang disebabkan oleh pengembang yang ceroboh menggunakan FirstOrDefault.

El Bung
sumber
-2

Saya bertanya kepada Google untuk penggunaan berbagai metode di GitHub. Ini dilakukan dengan menjalankan kueri pencarian Google untuk setiap metode dan membatasi kueri ke domain github.com dan ekstensi file .cs dengan menggunakan kueri "site: file github.com: cs ..."

Tampaknya metode First * lebih umum digunakan daripada metode Single *.

| Method               | Results |
|----------------------|---------|
| FirstAsync           |     315 |
| SingleAsync          |     166 |
| FirstOrDefaultAsync  |     357 |
| SingleOrDefaultAsync |     237 |
| FirstOrDefault       |   17400 |
| SingleOrDefault      |    2950 |
Fred
sumber
-8

Saya tidak mengerti mengapa Anda menggunakan FirstOrDefault(x=> x.ID == key)saat ini dapat mengambil hasil lebih cepat jika Anda gunakan Find(key). Jika Anda bertanya dengan kunci utama tabel, aturan praktisnya adalah untuk selalu menggunakan Find(key). FirstOrDefaultharus digunakan untuk hal-hal predikat seperti (x=> x.Username == username)dll.

ini tidak layak downvote karena judul pertanyaan tidak spesifik untuk LINQ pada DB atau LINQ untuk daftar / IEnumerable dll

Theron Govender
sumber
1
Namespace mana yang ada Find()?
p.campbell
Bisakah Anda memberi tahu kami? Masih menunggu jawaban.
Denny
Kata "IEnumerable" ada di baris pertama dari badan pertanyaan. Jika Anda hanya membaca judul dan bukan pertanyaan yang sebenarnya, dan memposting jawaban yang salah sebagai hasilnya, itu adalah kesalahan Anda dan alasan yang sangat sah untuk melakukan downvote IMO.
F1Krazy