LINQ Single ve First arasında


215

LINQ:

Sorgunun tek bir kayıt döndüreceğinden emin olduğumda Single()operatörü kullanmak daha verimli midir?First()

Bir fark var mı?

Yanıtlar:


312

Tek bir kayıt bekliyorsanız, kodunuzda açık olmak her zaman iyidir.

Başkalarının neden birini veya diğerini kullandığınızı yazdığını biliyorum, ama neden birini kullanmamanız gerektiğini açıklayacağımı düşündüm. diğerini kastettiğinizde .

Not: Kodumda genellikle kullanacağım FirstOrDefault() ve SingleOrDefault()bu farklı bir soru.

Örneğin, CustomersKompozit Anahtar ( ID, Lang) kullanarak farklı dillerde depolanan bir tabloyu ele alalım :

DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();

Yukarıdaki bu kod olası bir mantık hatası verir (izlenmesi zor). Birden fazla kayıt döndürür (birden fazla dilde müşteri kaydına sahip olduğunuzu varsayarsak), ancak her zaman yalnızca ilk kaydı döndürür ... bazen işe yarayabilir ... ancak diğerleri değil. Tahmin edilemez.

Amacınız tek Customerkullanımlık geri dönüş yapmak olduğundanSingle() ;

Aşağıdakiler bir istisna atar (bu durumda istediğiniz şeydir):

DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();

O zaman, sadece alnına vur ve kendine söyle ... OOPS! Dil alanını unuttum! Doğru sürüm aşağıdadır:

DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();

First() aşağıdaki senaryoda yararlıdır:

DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();

ONE nesnesini döndürür ve sıralamayı kullandığınız için, döndürülen en son kayıt olur.

Single()Her zaman 1 kayıt döndürmesi gerektiğini düşündüğünüzde kullanmak , mantık hatalarından kaçınmanıza yardımcı olacaktır.


76
Hem Tek hem de İlk yöntemleri filtreleme için bir ifade parametresi alabilir, bu nedenle Where işlevi gerekli değildir. Örnek: Müşteri müşterisi = db.Customers.Single (c => c.ID == 5);
Josh Noe

6
@JoshNoe - Merak ediyorum, arasında bir fark var customers.Where(predicate).Single() customers.Single(predicate)mı?
drzaus

9
@drzaus - Mantıken, hayır, ikisi de yükleme göre döndürülecek değerleri filtreler. Ancak, demontajı kontrol ettim ve Where (yüklem) .Single (), yaptığım basit durumda üç ekstra talimat içeriyor. Yani, ben IL uzmanı değilim, ancak müşteriler gibi görünüyor.Tek (yüklem) daha verimli olmalıdır.
Josh Noe

5
@JoshNoe ortaya çıktığı gibi tam tersi.
AgentFire

10
@AgentFire İfadenizi yedeklemek için
M. Mimpen

72

Single, ölçütlerle eşleşen birden fazla kayıt bulursa bir istisna atar. İlk önce her zaman listeden ilk kaydı seçer. Sorgu yalnızca 1 kayıt döndürürse, ile devam edebilirsiniz First().

InvalidOperationExceptionKoleksiyon boşsa her ikisi de bir istisna atar . Alternatif olarak kullanabilirsiniz SingleOrDefault(). Liste boşsa bu bir istisna oluşturmaz


29

Tek()

Bir sorgunun tek bir belirli öğesini döndürür

Kullanıldığında : Tam olarak 1 eleman bekleniyorsa; 0 veya 1'den fazla değil. Liste boşsa veya birden fazla öğeye sahipse, "Sıra birden fazla öğe içerir"

SingleOrDefault ()

Bir sorgunun tek bir belirli öğesini veya sonuç bulunamazsa varsayılan bir değer döndürür

Ne Zaman Kullanılır : 0 veya 1 eleman beklendiğinde. Listede 2 veya daha fazla öğe varsa bir istisna atar.

İlk()

Birden çok sonuç içeren bir sorgunun ilk öğesini döndürür.

Ne zaman Kullanılır : 1 veya daha fazla eleman beklendiğinde ve yalnızca ilk eleman istediğinizde. Listede öğe yoksa istisna atar.

FirstOrDefault ()

Herhangi bir miktarda öğeye sahip bir listenin ilk öğesini veya liste boşsa varsayılan değeri döndürür.

Ne zaman Kullanılır : Birden fazla eleman beklendiğinde ve yalnızca ilkini istediğinizde. Veya liste boştur ve belirtilen tür için varsayılan değerle aynıdır default(MyObjectType). Örneğin: liste türü ise, listeden list<int>ilk sayıyı veya liste boşsa 0 değerini döndürür. Öyleyse list<string>, listeden ilk dizeyi veya liste boşsa null değerini döndürür.


1
Güzel açıklama. Sadece kendinizin kullanabileceği değiştirecek Firstzaman 1 veya daha fazla öğe bekleniyor "1'den fazla" ve sadece, FirstOrDefaultelemanların herhangi bir miktar ile.
Andrew

18

Bu iki yöntem arasında ince, anlamsal bir fark vardır.

Singleİlk (ve yalnızca) öğeyi, bir öğe içermesi ve daha fazla içermemesi gereken bir diziden almak için kullanın . Dizide birden fazla öğe Singlevarsa, yalnızca tek bir öğe olması gerektiğini belirttiğiniz için çağrmanız bir istisna oluşmasına neden olur.

FirstHerhangi bir sayıda öğe içerebilen bir diziden ilk öğeyi almak için kullanın . Dizide birden fazla öğe varsa, dizideki Firstilk öğeye ihtiyacınız olduğunu ve daha fazla var olup olmadığını umursamadığınızı belirttiğiniz için çağrmanızın bir istisna oluşmasına neden olmaz.

Dizi hiçbir öğe içermiyorsa, her iki yöntem de en az bir öğenin bulunmasını beklediğinden, her iki yöntem çağrısı da istisnaların atılmasına neden olur.


17

Birden fazla öğe olması durumunda özellikle bir istisna oluşturulmasını istemiyorsanız, kullanınFirst() .

Her ikisi de verimli, ilk öğeyi al. First()biraz daha etkilidir çünkü ikinci bir öğe olup olmadığını kontrol etmeyi zahmet etmemektedir.

Tek fark, Single()numaralandırmada başlamak için sadece bir öğe olmasını ve birden fazla varsa bir istisna atmasıdır. Sen kullanmak .Single() özel olarak atılmış bir istisna istiyorsanız bu durumda.


14

Hatırlıyorsam, Single () ilkinden sonra başka bir öğe olup olmadığını kontrol eder (ve bu durumda bir istisna atar), First () ise aldıktan sonra durur. Dizi boşsa her ikisi de bir istisna atar.

Şahsen ben her zaman First () kullanıyorum.


2
SQL'de ürettikleri First () TOP 1 yapar ve Single () yanılmıyorsam TOP 2 yapar.
Matthijs Wessels

10

Performans konusunda: Bir iş arkadaşım ve ben Single - First (ya da SingleOrDefault vs FirstOrDefault) performansını tartışıyorduk ve First (ya da FirstOrDefault) 'un daha hızlı olacağı ve performansı geliştireceğimi tartışıyordum daha hızlı koş).

Stack Overflow'da bu konuyu tartışan birkaç yazı okudum. Bazıları, Single yerine First kullanarak küçük performans kazanımları olduğunu söylüyor. Bunun nedeni, First'in yalnızca ilk öğeyi döndürmesi ve Single'ın tüm sonuçları taraması gerektiğinden emin olmaktır (yani: öğeyi tablonun ilk satırında bulmuşsa, diğer tüm satırları koşulla eşleşen ve ardından bir hata atacak ikinci bir değer olmadığından emin olun). Sağlam bir zeminde olduğumu hissettim, “İlk” in “Bekar” dan daha hızlı olması bu yüzden bunu kanıtlamaya ve tartışmayı dinlendirmeye başladım.

Veritabanımda bir test oluşturdum ve 1.000.000 satır ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) (“999,9999” a “0” sayı dizeleriyle dolu) ekledim

Verileri yükledim ve kimliği birincil anahtar alanı olarak ayarladım.

LinqPad kullanarak amacım, Single ile 'Yabancı' veya 'Bilgi' üzerinde bir değer aradığınızda, İlk'i kullanmaktan çok daha kötü olacağını göstermekti.

Elde ettiğim sonuçları açıklayamıyorum. Hemen hemen her durumda, Single veya SingleOrDefault kullanmak biraz daha hızlıydı. Bu benim için mantıklı değil, ama bunu paylaşmak istedim.

Örnek: Aşağıdaki sorguları kullandım:

var q = TestTables.First(x=>x.Info == "314638") ;
//Vs.
Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise)

Ben 'Yabancı' anahtar alanında benzer sorguları denedim ki bu, İlk'in daha hızlı olduğunu, ancak Single'ın testlerimde her zaman biraz daha hızlı olduğunu düşünerek endekslenmedi.


2
Dizine alınmış bir alandaysa, veritabanının benzersiz olduğundan emin olmak için tarama yapması gerekmez. Olduğunu zaten biliyor. Yani ek yük yoktur ve sunucu tarafındaki tek ek yük sadece bir kaydın geri gelmesini sağlamaktır. Performans testleri kendim yapmak şu ya da bu şekilde kesin değil
mirhagk

1
IComparer tarafından kullanılmayan bir alanda arama yapıyorsanız, bu sonuçların karmaşık bir nesne üzerinde aynı olacağını düşünmüyorum.
Anthony Nichols

Bu başka bir soru olmalı. Senin kaynağını eklemeyi unutmayın testi .
Sinatr

5

Onlar farklı. Her ikisi de sonuç kümesinin boş olmadığını iddia ediyor, ancak single da 1'den fazla sonuç olmadığını iddia ediyor. Ben sadece 1 sonuç daha geri almak bir hata olduğundan ve muhtemelen böyle tedavi edilmelidir çünkü ben sadece 1 sonuç olmasını beklediğiniz durumlarda şahsen Tek kullanın.


5

Fark almak için basit bir örnek deneyebilirsiniz. 3. satırda istisna atılacaktır;

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

3

Tanıdığım birçok insan FirstOrDefault () kullanıyor, ancak SingleOrDefault () 'i daha çok kullanma eğilimindeyim çünkü birden fazla olsaydı genellikle bir çeşit veri tutarsızlığı olurdu. Bu LINQ-to-Objects ile ilgili.


-1

Çalışan varlığındaki kayıtlar:

Employeeid = 1: Bu kimliğe sahip yalnızca bir çalışan

Firstname = Robert: Bu ada sahip birden fazla çalışan

Employeeid = 10: Bu kimliğe sahip çalışan yok

Şimdi ne olduğunu Single()ve ne First()anlama geldiğini ayrıntılı olarak anlamak gerekiyor .

Tek()

Sorgu aşağıda kimin Çalışan dönecektir böylece Single (), benzersiz bir tablo var tek bir kayıt döndürmek için kullanılır employeed =1kimin tek Çalışan var çünkü Employeedbiz iki kayıtlarınız varsa 1'dir EmployeeId = 1o zaman bir hata atar (bkz için bir örnek kullandığımız ikinci sorguda aşağıdaki hata Firstname.

Employee.Single(e => e.Employeeid == 1)

Yukarıdaki tek bir kayıt dönecektir, hangi 1 employeeId

Employee.Single(e => e.Firstname == "Robert")

Yukarıdakiler bir istisna atar çünkü çoklu kayıtlar tablodadır FirstName='Robert'. İstisna

InvalidOperationException: Sıra birden fazla öğe içeriyor

Employee.Single(e => e.Employeeid == 10)

İd = 10 için kayıt olmadığı için bu yine bir istisna atar. İstisna olacak

InvalidOperationException: Sekans öğe içermiyor.

Çünkü EmployeeId = 10null döndürür, ancak kullandığımız Single()için bir hata atar. Boş hatayı işlemek için kullanmalıyız SingleOrDefault().

İlk()

İlk olarak () birden fazla kayıttan artan sıraya göre sıralanan kayıtlar birthdatedöndürülür , böylece en eski olan 'Robert' döndürülür.

Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")

Yukarıdaki DOB göre en eski olanı, Robert dönmelidir.

Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)

Yukarıdaki id = 10 kaydı olmadığından istisna atar. Boş bir istisnayı önlemek için kullanmak FirstOrDefault()yerine kullanmalıyız First().

Not: Yalnızca First()/ Single()boş bir değer döndüremeyeceğinden emin olduğumuzda / kullanabiliriz .

Her iki işlevde de tek bir istisna işleyecek olan SingleOrDefault () VEYA FirstOrDefault () kullanın , hiçbir kayıt bulunamaması durumunda null değerini döndürür.


Lütfen cevabınızı açıklayınız.
MrMaavin

1
@MrMaavin Güncelledim, lütfen bana söyleyin, şimdi sizin için anlaşılabilir mi?
Şerif
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.