LINQ: Filtreleme ölçütleriyle SingleOrDefault ve FirstOrDefault () ne zaman kullanılır?


506

IEnumerable uzantı yöntemlerini göz önünde bulundurun SingleOrDefault()veFirstOrDefault()

MSDN aşağıdakileri belgelemektedirSingleOrDefault :

Bir dizinin tek öğesini veya dizi boşsa varsayılan değeri döndürür; sekansta birden fazla eleman varsa bu yöntem bir istisna atar.

oysa FirstOrDefaultgelen MSDN'den (muhtemelen bir kullanıldığında OrderBy()ya da OrderByDescending(), hiç ya da yok)

Bir dizinin ilk öğesini döndürür

Bir kaç örnek sorguyu düşünün, bu iki yöntemi ne zaman kullanacağınız her zaman açık değildir:

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?

Soru

Kullanmaya karar verirken SingleOrDefault()ve FirstOrDefault()LINQ sorgularınızda hangi kuralları izliyorsunuz veya öneriyorsunuz ?

Yanıtlar:


466

Her kullandığınızda SingleOrDefault, sorgunun en fazla tek bir sonuçla sonuçlanması gerektiğini açıkça belirtirsiniz . Öte yandan, FirstOrDefaultkullanıldığında, sorgu herhangi bir miktarda sonuç döndürebilir, ancak yalnızca ilkini istediğinizi belirtirsiniz.

Ben semantiği çok farklı buluyorum ve uygun olanı kullanarak beklenen sonuçlara bağlı olarak okunabilirliği geliştiriyorum.


164
Çok önemli bir fark, SingleOrDefault'u birden fazla öğeye sahip bir sekansta kullanırsanız, bir istisna atar.
Kamran Bigdely

17
@kami bir istisna atmasaydı, tıpkı FirstOrDefault gibi olurdu. İstisna, bunu SingleOrDefault yapan şeydir. İyi bir noktaya getirmek ve farklılıklar tabutuna çivi koyar.
Fabio

17
Performans açısından, FirstOrDefault'un SingleOrDefault'dan yaklaşık 10 kat daha hızlı çalıştığını, 9.000.000 öğeden oluşan bir List <MyClass> kullanarak, sınıfın 2 tamsayı içerdiğini ve Func'un bu iki tamsayının bir aramasını içerdiğini söylemeliyim. Bir döngüde 200 kez arama var v = list'de 22 saniye sürdü.SingleOrDefault (x => x.Id1 == i && x.Id2 == i); ve var v = list.FirstOrDefault (x => x.Id1 == i && x.Id2 == i); yaklaşık 3 saniye
Chen

6
@BitsandBytesHandyman Eğer SignleOrDefault, dizi birden fazla öğe içerdiğinde bir istisna atmazsa, tıpkı FirstOrDefault gibi davranmaz. FirstOrDefault ilk öğeyi veya dizi boşsa null değerini döndürür. SingleOrDefault tek öğeyi döndürmeli veya sıra boşsa VEYA, birden fazla öğe içeriyorsa, bir istisna atmadan döndürmelidir.
Thanasis Ioannidis

2
@RSW Evet, bunun farkındayım. Yorumumu dikkatlice okurken SingleOrDefault'un ne yapması gerektiğini değil, ne yapması gerektiğini söylüyordum. Ama elbette, yapması gereken çok öznel. Benim için "SomethingOrDefault" kalıbı şu anlama gelir: "Something" değerini alın. "Bir şey" bir değer döndüremezse, varsayılan değeri döndürün. Bu, "Bir şey" in bir istisna atması durumunda bile varsayılan değerin döndürülmesi gerektiği anlamına gelir. Yani, Single'ın bir istisna attığı yerde, SingleOrDefault benim bakış açımdan varsayılan değeri döndürmelidir.
Thanasis Ioannidis

585

Sonuç kümeniz 0 kayıt döndürürse:

  • SingleOrDefault tür için varsayılan değeri döndürür (örneğin int için varsayılan değer 0'dır)
  • FirstOrDefault tür için varsayılan değeri döndürür

Sonuç kümeniz 1 kayıt döndürürse:

  • SingleOrDefault bu kaydı döndürür
  • FirstOrDefault bu kaydı döndürür

Sonuç kümeniz birçok kayıt döndürürse:

  • SingleOrDefault bir istisna atar
  • FirstOrDefault ilk kaydı döndürür

Sonuç:

Sonuç kümesi çok sayıda kayıt içeriyorsa bir istisnanın atılmasını istiyorsanız kullanın SingleOrDefault.

Sonuç kümesi ne olursa olsun her zaman 1 kayıt istiyorsanız, FirstOrDefault


6
Aslında çoğu zaman FirstOrDefault tercih edilir istisna istemek için nadir öneriyoruz. Vakaların o kadar sık ​​imo olmayacağını biliyorum.
MikeKulls

FirstOrDefaultilk kayıt döndürüldüğünde yeni kayıt (son) / eski kayıt (ilk) anlamına gelir?
Duk

@Duk, kayıtları nasıl sıraladığınıza bağlıdır. FirstOrDefault'u çağırmadan önce OrderBy () veya OrderByDescending () vb. OP'nin kod örneğine bakın.
Gan

5
Ben de bu yanıtı beğendim. Özellikle, istisnanın atılmasını istediğiniz bazı durumlar olduğu için, çünkü bu nadir davayı, başka bir yerde olmuyormuş gibi yerine başka bir yerde düzgün bir şekilde ele almak niyetindesiniz. İstisna istediğinizde bunu açıkça söylüyorsunuz ve diğerlerini de genel sistemi daha sağlam hale getirmeye zorlamaktasınız.
Francis Rodgers

Kişinin kolayca anlayabilmesi için çok açık bir şekilde ifade edilir.
Nirav Vasoya

243

Var

  • anlamsal bir fark
  • bir performans farkı

ikisinin arasında.

Anlamsal Fark:

  • FirstOrDefault potansiyel olarak birden fazla olan ilk öğeyi döndürür (veya yok ise varsayılan değerdir).
  • SingleOrDefaulttek bir öğe olduğunu varsayar ve öğeyi döndürür (veya yoksa, varsayılan değerdir). Birden fazla öğe sözleşme ihlalidir, bir istisna atılır.

Performans Farkı

  • FirstOrDefaultgenellikle daha hızlıdır, öğeyi bulana kadar yinelenir ve yalnızca bulamadığında tüm numaralandırmayı yinelemek zorundadır. Birçok durumda, bir öğeyi bulma olasılığı yüksektir.

  • SingleOrDefaultyalnızca bir elemanın olup olmadığını kontrol etmek gerekir ve bu nedenle her zaman numaralandırılabilir olanı tekrarlar. Kesin olmak gerekirse, ikinci bir öğe bulana ve bir istisna atana kadar tekrar eder. Ancak çoğu durumda ikinci bir unsur yoktur.

Sonuç

  • Kullanım FirstOrDefaultorada kaç nesne umursamazsak veya kontrol etmeye benzersizliğini göze alamaz zaman (çok büyük koleksiyonunda örneğin). Koleksiyona öğe ekleme konusundaki benzersizliği kontrol ettiğinizde, bu öğeleri ararken tekrar kontrol etmek çok pahalı olabilir.

  • Kullanım SingleOrDefaultçok fazla performans hakkında bakım ve emin bir tek unsur varsayımı okuyucuya açık ve zamanında teslim olduğunu yapmak istemiyorsanız.

Uygulamada, performansı artırmak için First/ FirstOrDefaultgenellikle tek bir öğe varsaydığınız durumlarda bile kullanılır . Okunabilirliği (tek bir öğenin varsayımını belirttiğinden) ve istikrarı (kontrol ettiği için) geliştirebileceğini Single/ SingleOrDefaultiyileştirebileceğini ve uygun şekilde kullanabileceğini hatırlamanız gerekir .


16
+1 "veya tekliği kontrol etmeyi göze alamadığınızda (örneğin, çok büyük bir koleksiyonda)." . Bunu arıyordum. Ben de sorgu yaparken, yerine ekleme ve / ve tasarım yoluyla zorlama özgünlük eklemek istiyorum !
Nawaz

Tahmin edebiliyorum SingleOrDefaultNesneler Linq kullanırken pek çok nesnenin üzerinde yineler değil, SingleOrDefaultLinq örneğin bir veritabanına konuşuyor Çoğu 2 öğeleri üzerinde yineleme zorunda? Sadece merak ..
Memet Olsen

3
@memetolsen LINQ to SQL ile ikisinin kod tükürüğünü düşünün - FirstOrDefault Top 1'i kullanır. SingleOrDefault Top 2'yi kullanır.
Jim Wooley

@JimWooley Sanırım 'numaralandırılabilir' kelimesini yanlış anladım. Stefan'ın C # demek istediğini sanıyordum Enumerable.
Memet Olsen

1
@memetolsen orijinal cevap açısından doğru, yorumunuzu veritabanına atıfta bulundu, bu yüzden sağlayıcıdan neler sunuyordu. .Net kodu yalnızca 2 değerin üzerinde yinelemesine rağmen, veritabanı, ölçütleri karşılayan ikinci değere ulaşana kadar gereken sayıda kaydı ziyaret eder.
Jim Wooley

76

Kimse SQL'de çevrilen FirstOrDefault'un TOP 1 kaydı yaptığını ve SingleOrDefault'un TOP 2 yaptığını söylememiştir, çünkü 1'den fazla kayıt olduğunu bilmelidir.


3
LinqPad ve VS aracılığıyla bir SingleOrDefault çalıştırdığımda, asla SELECT TOP 2'yi almadım, FirstOrDefault ile SELECT TOP 1 elde edebildim, ancak size söyleyebildiğim kadarıyla SELECT TOP 2'yi alamadım.
Jamie R Rytlewski

Hey ben de linqpad denenmiş ve tamamen tüm satırları getir çünkü sql sorgu beni korkuttu. Bunun nasıl olabileceğinden emin değilim?
AnyOne

1
Bu tamamen kullanılan LINQ sağlayıcısına bağlıdır. Örneğin, LINQ to SQL ve LINQ to Entities, SQL'e farklı şekillerde çeviri yapabilir. Ben sadece IQ MySql sağlayıcı ile LINQPad denedim ve hiçbir şey FirstOrDefault()eklerken LIMIT 0,1ise SingleOrDefault()ekler.
Lucas

1
EF Core 2.1, FirstOrDefault öğesini SELECT TOP (1), SingleOrDefault öğesini SELECT TOP (2) olarak
çevirir

19

LINQ -> SQL için:

SingleOrDefault

  • "userid = 1" olan kullanıcılardan "select *" gibi bir sorgu oluşturur
  • Eşleşen kaydı seçin, birden fazla kayıt bulunursa istisna atar
  • Birincil / benzersiz anahtar sütununa dayalı veri alıyorsanız kullanın

FirstOrDefault

  • "userid = 1 olan kullanıcılardan top 1 * seç" gibi bir sorgu oluşturur
  • İlk eşleşen satırları seçin
  • Birincil / benzersiz olmayan anahtar sütununa dayalı veri alıyorsanız kullanın

"Tüm eşleşen satırları seç" i SingleOrDefault
Saim Abdullah

10

SingleOrDefaultMantığımın sıfır veya bir sonuç olacağını belirlediği durumlarda kullanıyorum . Daha fazlası varsa, yardımcı olan bir hata durumudur.


3
Genellikle, SingleOrDefault () yönteminin sonuç kümesine doğru filtrelemeyi uygulamadığım veya temel alınan verilerde yinelemelerde bir sorun olduğu durumları vurguladığını görüyorum. Sık sık kendimi First () yöntemleri üzerinde Single () ve SingleOrDefault () kullanarak bulmuyorum.
TimS

Büyük bir numaralandırılabilir, ancak bir veritabanıyla (örn. SQL Server) konuşurken, LINQ to Objects üzerinde Single () ve SingleOrDefault () için performans sonuçları vardır. Çağrı pahalı olmamalı ve ben hızlı başarısız ve First () veya FirstOrDefault () çağırırken yanlış yinelenen alarak başka veri sorunları yerine veri sorunu bulmak istiyorum.
heartlandcoder

5

SingleOrDefault: "En fazla" sorgusuyla veya varsayılanla eşleşen bir öğe olduğunu söylüyorsunuz FirstOrDefault: Sorguyla veya varsayılanla eşleşen "En az" bir öğe olduğunu söylüyorsunuz

Bir dahaki sefere seçmeniz gerektiğini ve muhtemelen akıllıca seçeceğinizi söyleyin. :)


5
Aslında hiçbir sonuca sahip olmak, FirstOrDefault. More correctly: FirstOrDefault` = Herhangi bir sonuç sayısının mükemmel kabul edilebilir kullanımıdır, ancak sadece birincisini önemsiyorum, sonuç da olmayabilir. SingleOrDefault= 1 veya 0 sonuç var, eğer daha fazlası varsa bir yerde bir hata var demektir. First= En az bir sonuç var ve bunu istiyorum. Single= Tam olarak 1 sonuç var, daha fazla, daha az değil ve bunu istiyorum.
Davy8

4

Sizin durumlarınızda aşağıdakileri kullanırdım:

ID == 5'e göre seçin: Burada SingleOrDefault'u kullanmanız uygundur, çünkü bir [veya hiçbiri] varlık beklersiniz, eğer 5 kimliğine sahip birden fazla varlık varsa, yanlış bir şey vardır ve kesinlikle istisnai bir durum vardır.

adı "Bobby" olan insanları ararken, birden fazla olabilir (büyük olasılıkla düşünürdüm), bu yüzden ne Tek ne de İlk'i kullanmamalısınız, sadece Nerede operasyonuyla ("Bobby" çok fazla dönerse) kullanıcının aramasını hassaslaştırması veya döndürülen sonuçlardan birini seçmesi gerekir)

oluşturma tarihine göre sipariş aynı zamanda bir Where-operasyonu ile gerçekleştirilmelidir (yalnızca tek bir varlığa sahip olma olasılığı düşüktür, sıralama çok işe yaramaz;) Bu, TÜM varlıkların sıralanmasını istediğiniz anlamına gelir - sadece ONE istiyorsanız, FirstOrDefault kullanın, Birden fazla varlığınız varsa her seferinde tek atış yapar.


3
Katılmıyorum. Veritabanı kimliğiniz birincil bir anahtarsa, veritabanı zaten benzersizliği uygular. Veritabanının her sorguda işini yapıp yapmadığını kontrol etmek için CPU döngüsünü boşa harcamak sadece aptalca.
John Henckel

4

Her ikisi de eleman operatörleridir ve bir sekanstan tek bir eleman seçmek için kullanılırlar. Ancak aralarında küçük bir fark var. Birden fazla öğe karşılanırsa, SingleOrDefault () operatörü, FirstOrDefault () olarak aynı durum için herhangi bir istisna atmayacak koşuldan bir istisna atar. İşte örnek.

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

2
"Aralarında küçük bir fark var" - Bu çok önemli!
Nikhil Vartak

3

Son örneğinizde:

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

Evet öyle. Kullanmaya çalışırsanız SingleOrDefault()ve sorgu kayıt daha fazla sonuç alırsanız ve istisna. Güvenle kullanabileceğiniz SingleOrDefault()tek zaman sadece 1 ve sadece 1 sonuç beklediğiniz zamandır ...


Bu doğru. 0 sonuç alırsanız, bir istisna da alırsınız.
Dennis Rongo

1

Şimdi anladığım kadarıyla, SingleOrDefaultbenzersiz olduğu garanti edilen veriyi, yani birincil anahtar gibi DB kısıtlamaları tarafından zorunlu tuttuğunuzda sorgulayacaksanız iyi olacak.

Veya birincil anahtarı sorgulamanın daha iyi bir yolu var mı?

TableAcc'imin

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

ve ben sorgulamak istiyorum AccountNumber 987654, kullanıyorum

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

1

Bence FirstOrDefaultçok fazla kullanılıyor. Verileri filtrelediğiniz çoğu durumda, mantıksal koşulla eşleşen bir öğe koleksiyonunu veya benzersiz bir tanımlayıcıyla (kullanıcı, kitap, posta vb. Gibi) tek bir benzersiz öğeyi geri almayı beklersiniz. neden FirstOrDefault()bir kod kokusu olduğunu söyleyebiliriz ki, yanlış bir şey olduğu için değil, çok sık kullanıldığı için. Bu blog gönderisi konuyu ayrıntılı olarak araştırıyor. IMO çoğu zaman SingleOrDefault()çok daha iyi bir alternatiftir, bu nedenle bu hataya dikkat edin ve sözleşmenizi ve beklentilerinizi açıkça temsil eden en uygun yöntemi kullandığınızdan emin olun.


-1

Cevaplarda kaçırılan bir şey ....

Birden fazla sonuç varsa, siparişi olmayan FirstOrDefault, sunucu tarafından kullanılan dizin stratejisinin gerçekleşmesine bağlı olarak farklı sonuçları geri getirebilir.

Şahsen FirstOrDefault kodunu görmeye dayanamıyorum çünkü bana göre geliştirici sonuçları umursamadı diyor. Gerçi bir sipariş ile en son / en erken uygulama yolu olarak yararlı olabilir. FirstOrDefault kullanan dikkatsiz geliştiricilerin neden olduğu birçok sorunu düzeltmek zorunda kaldım.


-2

Google'ı GitHub'daki farklı yöntemlerin kullanımı için sorguladım. Bu, her yöntem için bir Google arama sorgusu çalıştırarak ve sorguyu "site: github.com dosya: cs ..." sorgusunu kullanarak github.com etki alanı ve .cs dosya uzantısıyla sınırlayarak yapılır.

İlk * yöntemlerin Single * yöntemlerinden daha yaygın kullanıldığı görülmektedir.

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

-8

Bunun FirstOrDefault(x=> x.ID == key)sonuçları daha hızlı alabileceği zaman neden kullandığınızı anlamıyorum Find(key). Tablonun Birincil anahtarıyla sorgu yapıyorsanız, temel kural her zaman kullanmaktır Find(key). FirstOrDefaultgibi yüklemler için kullanılmalıdır (x=> x.Username == username).

sorunun başlığı DB'deki linq veya List / IEnumerable vb.


1
Hangi ad alanı var Find()?
p.campbell

Bize söyler misiniz lütfen? Hala bir cevap bekliyorum.
Denny


"IEnumerable" kelimesi soru gövdesinin ilk satırındadır. Eğer sadece başlık değil gerçek soruyu okuyun ve bir hata bir sonucu olarak yanlış bir yanıt gönderdi olursa ve IMO downvote için mükemmel meşru bir neden.
F1Krazy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.