LINQ Single vs First

214

LINQ:

Apakah lebih efisien menggunakan Single()operator First()kapan saja saya tahu pasti bahwa kueri akan mengembalikan satu catatan ?

Apakah ada perbedaan?

Ian Vink
sumber

Jawaban:

311

Jika Anda mengharapkan catatan tunggal, selalu baik untuk menjadi eksplisit dalam kode Anda.

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 Customersdalam 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 CustomerPenggunaan 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.

Terkuat
sumber
76
Metode Single dan First dapat mengambil parameter ekspresi untuk pemfilteran, jadi fungsi Di mana tidak perlu. Contoh: Pelanggan pelanggan = db.Customers.Single (c => c.ID == 5);
Josh Noe
6
@ JoshNoe - Saya ingin tahu, apakah ada perbedaan antara customers.Where(predicate).Single() customers.Single(predicate)?
drzaus
9
@drzaus - Secara logis, tidak, keduanya menyaring nilai yang akan dikembalikan berdasarkan predikat. Namun, saya memeriksa pembongkaran, dan Di mana (predikat). Single () memiliki tiga instruksi tambahan dalam kasus sederhana yang saya buat. Jadi, sementara saya bukan ahli IL, tetapi tampaknya pelanggan. Tunggal (predikat) harus lebih efisien.
Josh Noe
5
@ Astaga, ini benar-benar kebalikannya.
AgentFire
10
@ AgentFire Untuk mencadangkan pernyataan Anda
M. Mimpen
72

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 InvalidOperationExceptionpengecualian jika koleksinya kosong. Atau Anda bisa menggunakan SingleOrDefault(). Ini tidak akan menghasilkan pengecualian jika daftar kosong

Selalu Pemrogram
sumber
29

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 daftar list<int>itu akan mengembalikan angka pertama dari daftar atau 0 jika daftar kosong. Jika ya list<string>, itu akan mengembalikan string pertama dari daftar atau nol jika daftar kosong.

Diego Mendes
sumber
1
Penjelasan yang bagus. Saya hanya akan mengubah yang dapat Anda gunakan Firstketika 1 atau lebih elemen diharapkan , tidak hanya "lebih dari 1", dan FirstOrDefaultdengan jumlah elemen apa pun.
Andrew
18

Ada perbedaan semantik dan halus antara kedua metode ini.

Gunakan Singleuntuk mengambil elemen pertama (dan hanya) dari urutan yang seharusnya mengandung satu elemen dan tidak lebih. Jika urutan memiliki lebih dari pada elemen permintaan Anda Singleakan menyebabkan pengecualian dilempar karena Anda menunjukkan bahwa hanya ada satu elemen.

Gunakan Firstuntuk mengambil elemen pertama dari urutan yang dapat berisi sejumlah elemen. Jika urutan memiliki lebih dari pada elemen permintaan Anda Firsttidak 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.

Andrew Hare
sumber
17

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.

Patrick Karcher
sumber
14

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 ().

Etienne de Martel
sumber
2
Dalam SQL mereka menghasilkan First () melakukan TOP 1 dan Single () melakukan TOP 2 jika saya tidak salah.
Matthijs Wessels
10

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.

Marcus Talcott
sumber
2
Jika berada di bidang yang diindeks dari database tidak perlu melakukan pemindaian untuk memastikan itu unik. Sudah tahu itu. Jadi tidak ada overhead di sana, dan satu-satunya overhead di sisi server adalah memastikan hanya satu catatan yang kembali. Melakukan tes kinerja sendiri itu tidak konklusif satu arah atau yang lain
mirhagk
1
Saya tidak berpikir hasil ini akan sama pada objek yang kompleks jika Anda mencari di bidang yang tidak digunakan oleh IComparer.
Anthony Nichols
Itu seharusnya menjadi pertanyaan lain. Pastikan untuk menyertakan sumber tes Anda .
Sinatr
5

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.

jaltiere
sumber
5

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);
Chauskin Rodion
sumber
3

Banyak orang yang saya kenal menggunakan FirstOrDefault (), tetapi saya cenderung menggunakan SingleOrDefault () lebih sering karena itu akan menjadi semacam ketidakkonsistenan data jika ada lebih dari satu. Ini berhubungan dengan LINQ-to-Objects.

Matt H
sumber
-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 =1karena kita hanya memiliki satu Karyawan yang Employeed1. Jika kita memiliki dua catatan untuk EmployeeId = 1maka 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 = 10itu 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 birthdatesehingga 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.

Sheriff
sumber
Tolong jelaskan jawaban Anda.
MrMaavin
1
@MrMaavin saya telah memperbarui, mohon beri tahu saya apakah ini bisa dimengerti sekarang untuk Anda?
Sheriff