LINQ Single vs First

214

LINQ:

Ist es effizienter, den Single()Operator zu verwenden, First()wenn ich sicher bin, dass die Abfrage einen einzelnen Datensatz zurückgibt?

Ist da ein Unterschied?

Ian Vink
quelle

Antworten:

311

Wenn Sie einen einzelnen Datensatz erwarten, ist es immer gut, in Ihrem Code explizit zu sein.

Ich weiß, dass andere geschrieben haben, warum Sie das eine oder das andere verwenden, aber ich dachte, ich würde veranschaulichen, warum Sie das eine NICHT verwenden sollten, wenn Sie das andere meinen .

Hinweis: In meinem Code, ich in der Regel verwenden FirstOrDefault()und , SingleOrDefault()aber das ist eine andere Frage.

Nehmen Sie zum Beispiel eine Tabelle, die Customersmit einem zusammengesetzten Schlüssel ( ID, Lang) in verschiedenen Sprachen gespeichert wird :

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

Dieser obige Code führt einen möglichen Logikfehler ein (schwer zu verfolgen). Es wird mehr als ein Datensatz zurückgegeben (vorausgesetzt, Sie haben den Kundendatensatz in mehreren Sprachen), aber es wird immer nur der erste zurückgegeben ... was manchmal funktioniert ... aber nicht andere. Es ist unvorhersehbar.

Da Sie beabsichtigen, eine einmalige CustomerVerwendung zurückzugeben Single();

Folgendes würde eine Ausnahme auslösen (was in diesem Fall gewünscht wird):

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

Dann schlägst du dich einfach auf die Stirn und sagst dir ... OOPS! Ich habe das Sprachfeld vergessen! Es folgt die richtige Version:

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

First() ist im folgenden Szenario nützlich:

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

Es wird EIN Objekt zurückgegeben, und da Sie die Sortierung verwenden, ist es der letzte Datensatz, der zurückgegeben wird.

Mit Single()wenn Sie es ausdrücklich immer 1 Datensatz zurückkehren wird dazu beitragen , logische Fehler fühlen Sie vermeiden sollten.

Armstrongest
quelle
76
Sowohl die Single- als auch die First-Methode können einen Ausdrucksparameter zum Filtern verwenden, sodass die Where-Funktion nicht erforderlich ist. Beispiel: Kunde customer = db.Customers.Single (c => c.ID == 5);
Josh Noe
6
@ JoshNoe - Ich bin neugierig, gibt es einen Unterschied zwischen customers.Where(predicate).Single() customers.Single(predicate)?
Drzaus
9
@drzaus - Logischerweise filtern beide die zurückzugebenden Werte basierend auf dem Prädikat. Ich habe jedoch die Demontage überprüft und Where (Prädikat) .Single () hat in dem einfachen Fall, den ich gemacht habe, drei zusätzliche Anweisungen. Ich bin zwar kein IL-Experte, aber es scheint, dass Kunden.Einzel (Prädikat) effizienter sein sollten.
Josh Noe
5
@ JoshNoe es ist ein ziemliches Gegenteil, wie sich herausstellt.
AgentFire
10
@ AgentFire Um Ihre Aussage zu
sichern
72

Single löst eine Ausnahme aus, wenn mehr als ein Datensatz gefunden wird, der den Kriterien entspricht. Zuerst wird immer der erste Datensatz aus der Liste ausgewählt. Wenn die Abfrage nur 1 Datensatz zurückgibt, können Sie fortfahren First().

Beide lösen eine InvalidOperationExceptionAusnahme aus, wenn die Sammlung leer ist. Alternativ können Sie verwenden SingleOrDefault(). Dies löst keine Ausnahme aus, wenn die Liste leer ist

AlwaysAProgrammer
quelle
29

Single()

Gibt ein einzelnes bestimmtes Element einer Abfrage zurück

Bei Verwendung : Wenn genau 1 Element erwartet wird; nicht 0 oder mehr als 1. Wenn die Liste leer ist oder mehr als ein Element enthält, wird eine Ausnahme "Sequenz enthält mehr als ein Element" ausgelöst.

SingleOrDefault ()

Gibt ein einzelnes bestimmtes Element einer Abfrage oder einen Standardwert zurück, wenn kein Ergebnis gefunden wird

Bei Verwendung : Wenn 0 oder 1 Elemente erwartet werden. Es wird eine Ausnahme ausgelöst, wenn die Liste 2 oder mehr Elemente enthält.

Zuerst()

Gibt das erste Element einer Abfrage mit mehreren Ergebnissen zurück.

Bei Verwendung : Wenn 1 oder mehrere Elemente erwartet werden und Sie nur das erste möchten. Es wird eine Ausnahme ausgelöst, wenn die Liste keine Elemente enthält.

FirstOrDefault ()

Gibt das erste Element einer Liste mit einer beliebigen Anzahl von Elementen oder einen Standardwert zurück, wenn die Liste leer ist.

Bei Verwendung : Wenn mehrere Elemente erwartet werden und Sie nur das erste möchten. Oder die Liste ist leer und Sie möchten einen Standardwert für den angegebenen Typ wiedefault(MyObjectType) . Beispiel: Wenn der Listentyp ist list<int>, wird die erste Nummer aus der Liste zurückgegeben oder 0, wenn die Liste leer ist. Wenn dies der Fall ist list<string>, wird die erste Zeichenfolge aus der Liste zurückgegeben oder null, wenn die Liste leer ist.

Diego Mendes
quelle
1
Schöne Erklärung. Ich würde nur ändern, dass Sie verwenden können, Firstwenn 1 oder mehr Elemente erwartet werden , nicht nur "mehr als 1" und FirstOrDefaultmit einer beliebigen Anzahl von Elementen.
Andrew
18

Es gibt einen subtilen semantischen Unterschied zwischen diesen beiden Methoden.

Verwenden Sie Singlediese Option , um das erste (und einzige) Element aus einer Sequenz abzurufen, die ein Element und nicht mehr enthalten soll. Wenn die Sequenz mehr als ein Element enthält, wird durch Ihren Aufruf von Singleeine Ausnahme ausgelöst, da Sie angegeben haben, dass nur ein Element vorhanden sein soll.

Verwenden Sie Firstdiese Option , um das erste Element aus einer Sequenz abzurufen, die eine beliebige Anzahl von Elementen enthalten kann. Wenn die Sequenz mehr als ein Element enthält, wird Ihr Aufruf vonFirst keine Ausnahme ausgelöst, da Sie angegeben haben, dass Sie nur das erste Element in der Sequenz benötigen und es egal ist, ob weitere vorhanden sind.

Wenn die Sequenz keine Elemente enthält, werden bei beiden Methodenaufrufen Ausnahmen ausgelöst, da beide Methoden erwarten, dass mindestens ein Element vorhanden ist.

Andrew Hare
quelle
17

Wenn Sie nicht ausdrücklich möchten, dass eine Ausnahme für den Fall ausgelöst wird, dass mehr als ein Element vorhanden ist, verwenden SieFirst() .

Beide sind effizient, nehmen Sie den ersten Punkt. First()ist etwas effizienter, da es nicht die Mühe macht, zu überprüfen, ob es ein zweites Element gibt.

Der einzige Unterschied besteht darin, Single()dass zunächst nur ein Element in der Aufzählung erwartet wird und eine Ausnahme ausgelöst wird, wenn mehr als ein Element vorhanden ist. Sie verwenden .Single() diese Option , wenn in diesem Fall speziell eine Ausnahme ausgelöst werden soll.

Patrick Karcher
quelle
14

Wenn ich mich erinnere, prüft Single (), ob nach dem ersten ein anderes Element vorhanden ist (und löst eine Ausnahme aus, wenn dies der Fall ist), während First () nach dem Abrufen stoppt. Beide lösen eine Ausnahme aus, wenn die Sequenz leer ist.

Persönlich benutze ich immer First ().

Etienne de Martel
quelle
2
In der SQL produzieren sie First () macht TOP 1 und Single () macht TOP 2, wenn ich mich nicht irre.
Matthijs Wessels
10

In Bezug auf die Leistung: Ein Mitarbeiter und ich diskutierten die Leistung von Single vs First (oder SingleOrDefault vs FirstOrDefault) und ich argumentierte für den Punkt, dass First (oder FirstOrDefault) schneller sein und die Leistung verbessern würde (ich möchte unsere App entwickeln) schneller laufen).

Ich habe mehrere Beiträge zu Stack Overflow gelesen, in denen dies diskutiert wird. Einige sagen, dass es kleine Leistungssteigerungen gibt, wenn First anstelle von Single verwendet wird. Dies liegt daran, dass First einfach das erste Element zurückgibt, während Single alle Ergebnisse scannen muss, um sicherzustellen, dass kein Duplikat vorhanden ist (dh, wenn das Element in der ersten Zeile der Tabelle gefunden wird, wird weiterhin jede zweite Zeile gescannt Stellen Sie sicher, dass es keinen zweiten Wert gibt, der der Bedingung entspricht, die dann einen Fehler auslösen würde. Ich hatte das Gefühl, auf einem soliden Boden zu stehen, da „First“ schneller als „Single“ war, und machte mich daran, dies zu beweisen und die Debatte zu beenden.

Ich habe einen Test in meiner Datenbank eingerichtet und 1.000.000 Zeilen ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) hinzugefügt (gefüllt mit Zeichenfolgen von „0“ bis „999.9999“.

Ich habe die Daten geladen und die ID als Primärschlüsselfeld festgelegt.

Mit LinqPad wollte ich zeigen, dass es viel schlimmer wäre, wenn Sie mit Single nach einem Wert für 'Foreign' oder 'Info' suchen, als mit First.

Ich kann die Ergebnisse nicht erklären. In fast allen Fällen war die Verwendung von Single oder SingleOrDefault etwas schneller. Das ergibt für mich keinen logischen Sinn, aber ich wollte das teilen.

Beispiel: Ich habe die folgenden Abfragen verwendet:

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

Ich habe ähnliche Abfragen im Schlüsselfeld "Fremd" versucht, bei denen es sich nicht um indizierte Überlegungen handelte, die beweisen würden, dass First schneller ist, Single jedoch in meinen Tests immer etwas schneller.

Marcus Talcott
quelle
2
Wenn es sich in einem indizierten Feld befindet, muss die Datenbank keinen Scan durchführen, um sicherzustellen, dass es eindeutig ist. Es weiß bereits, dass es ist. Es gibt dort also keinen Overhead, und der einzige Overhead auf der Serverseite besteht darin, sicherzustellen, dass nur ein Datensatz zurückkommt. Selbst Leistungstests zu machen ist auf die eine oder andere Weise nicht schlüssig
mirhagk
1
Ich denke nicht, dass diese Ergebnisse für ein komplexes Objekt gleich wären, wenn Sie in einem Feld suchen würden, das nicht von IComparer verwendet wird.
Anthony Nichols
Das sollte eine andere Frage sein. Stellen Sie sicher, dass Sie die Quelle Ihres Tests angeben .
Sinatr
5

Sie sind anders. Beide behaupten, dass die Ergebnismenge nicht leer ist, aber single behauptet auch, dass es nicht mehr als 1 Ergebnis gibt. Ich persönlich verwende Single in Fällen, in denen ich nur ein Ergebnis erwarte, nur weil es ein Fehler ist, mehr als ein Ergebnis zurückzubekommen, und wahrscheinlich als solches behandelt werden sollte.

Jaltiere
quelle
5

Sie können ein einfaches Beispiel ausprobieren, um einen Unterschied zu erzielen. Ausnahme wird in Zeile 3 ausgelöst;

        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
quelle
3

Viele Leute, die ich kenne, verwenden FirstOrDefault (), aber ich verwende SingleOrDefault () häufiger, da es häufig zu einer Art Dateninkonsistenz kommen würde, wenn es mehr als eine gäbe. Dies betrifft jedoch LINQ-to-Objects.

Matt H.
quelle
-1

Die Datensätze in der Entität Mitarbeiter:

Employeeid = 1: Nur ein Mitarbeiter mit dieser ID

Firstname = Robert: Mehr als ein Mitarbeiter mit diesem Namen

Employeeid = 10: Kein Mitarbeiter mit dieser ID

Jetzt ist es notwendig zu verstehen, was Single()und was First()im Detail bedeutet.

Single()

Single () wird verwendet, um einen einzelnen Datensatz zurückzugeben, der eindeutig in einer Tabelle vorhanden ist. Die folgende Abfrage gibt also den Mitarbeiter zurück, dessen, employeed =1weil wir nur einen Mitarbeiter haben, dessen 1 Employeedist. Wenn wir zwei Datensätze für EmployeeId = 1haben, wird ein Fehler ausgegeben (siehe die Fehler unten in der zweiten Abfrage, für die wir ein Beispiel verwenden Firstname.

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

Das obige gibt einen einzelnen Datensatz zurück, der 1 hat employeeId

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

Das Obige löst eine Ausnahme aus, da mehrere Datensätze in der Tabelle für enthalten sind FirstName='Robert'. Die Ausnahme wird sein

InvalidOperationException: Die Sequenz enthält mehr als ein Element

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

Dies löst erneut eine Ausnahme aus, da für id = 10 kein Datensatz vorhanden ist. Die Ausnahme wird sein

InvalidOperationException: Die Sequenz enthält keine Elemente.

Denn EmployeeId = 10es wird null zurückgeben, aber während wir es benutzen Single(), wird es einen Fehler auslösen. Um Nullfehler zu behandeln, sollten wir verwenden SingleOrDefault().

Zuerst()

First () gibt aus mehreren Datensätzen die entsprechenden Datensätze zurück, die in aufsteigender Reihenfolge sortiert sind, birthdatesodass 'Robert' zurückgegeben wird, der am ältesten ist.

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

Oben sollte der älteste zurückgegeben werden, Robert gemäß DOB.

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

Oben wird eine Ausnahme ausgelöst, da kein Datensatz für id = 10 vorhanden ist. Um eine Null - Ausnahme zu vermeiden wir verwenden sollten , FirstOrDefault()als vielmehr First().

Hinweis: Wir können nur First()/ verwenden, Single()wenn wir absolut sicher sind, dass kein Nullwert zurückgegeben werden kann.

Verwenden Sie in beiden Funktionen SingleOrDefault () ODER FirstOrDefault (), das eine Null-Ausnahme behandelt. Wenn kein Datensatz gefunden wird, wird Null zurückgegeben.

Sheriff
quelle
Bitte erläutern Sie Ihre Antwort.
MrMaavin
1
@ MrMaavin Ich habe aktualisiert, bitte lassen Sie mich wissen, ist es jetzt für Sie verständlich?
Sheriff