Linq-Code zur Auswahl eines Elements

105

Ich schreibe so viel Code, um ein passendes Element auszuwählen

var item = (from x in Items where x.Id == 123 select x).First();

Gibt es eine sauberere Art, dies zu tun, oder ist dies so präzise, ​​wie ich es bekommen werde?

EDIT: Hätte sagen sollen "Sauberer Weg mit Linq-Syntax". Ich war mir der Lambda-Syntax bereits bewusst und es sieht so aus, als wäre dies tatsächlich der einzige Weg. Ich habe jedoch einige nützliche Informationen erhalten, also danke an alle, die geantwortet haben.

Mikey Hogarth
quelle
5
Persönlich vermeide ich Single()und SingleOrDefault()WENN ich weiß, dass die Daten bereits eindeutig sind (z. B. aus einer Datenbank mit dieser Einschränkung usw.), da Single()sie gezwungen sind, den Rest der Liste zu scannen, um ein mögliches Duplikat zu finden, aber das bin ich. Wenn Sie an dieser Stelle Ihre Eindeutigkeit erzwingen müssen, verwenden Sie Single()family. Wenn nicht, verwenden Sie First()family.
James Michael Hare

Antworten:

176

Abhängig davon, wie sehr Ihnen die Linq-Abfragesyntax gefällt, können Sie die Erweiterungsmethoden direkt wie folgt verwenden:

var item = Items.First(i => i.Id == 123);

Wenn Sie keinen Fehler auslösen möchten, wenn die Liste leer ist, geben Sie FirstOrDefaultden Standardwert für den Elementtyp ( nullfür Referenztypen) zurück:

var item = Items.FirstOrDefault(i => i.Id == 123);

if (item != null)
{
    // found it
}

Single()und SingleOrDefault()kann auch verwendet werden, aber wenn Sie aus einer Datenbank lesen oder etwas, das bereits Einzigartigkeit garantiert, würde ich mich nicht darum kümmern, da es die Liste scannen muss, um zu sehen, ob es Duplikate und Würfe gibt. First()und FirstOrDefault()beim ersten Spiel anhalten, damit sie effizienter sind.

Von den First()und Single()Familie, hier, wo sie werfen:

  • First() - wirft, wenn leer / nicht gefunden, wirft nicht, wenn doppelt vorhanden
  • FirstOrDefault() - Gibt die Standardeinstellung zurück, wenn leer / nicht gefunden, wird nicht geworfen, wenn doppelt vorhanden
  • Single() - wirft, wenn leer / nicht gefunden, wirft, wenn ein Duplikat vorhanden ist
  • SingleOrDefault() - Gibt den Standardwert zurück, wenn leer / nicht gefunden. Wird ausgelöst, wenn ein Duplikat vorhanden ist
James Michael Hare
quelle
1
Ich denke, Ihnen fehlen dort zwei Gleichheitszeichen. Sollte seini.Id == 123
davehale23
18

FirstOrDefault oder SingleOrDefault können nützlich sein, abhängig von Ihrem Szenario und davon, ob Sie mit null oder mehr als einer Übereinstimmung umgehen möchten:

FirstOrDefault: Gibt das erste Element einer Sequenz oder einen Standardwert zurück, wenn kein Element gefunden wird.

SingleOrDefault: Gibt das einzige Element einer Sequenz oder einen Standardwert zurück, wenn die Sequenz leer ist. Diese Methode löst eine Ausnahme aus, wenn die Sequenz mehr als ein Element enthält

Ich weiß nicht, wie das in einer linq 'from'-Abfrage funktioniert, aber in der Lambda-Syntax sieht es so aus:

var item1 = Items.FirstOrDefault(x => x.Id == 123);
var item2 = Items.SingleOrDefault(x => x.Id == 123);
stuartd
quelle
Welches ist in Bezug auf die DB-Zeit am effizientesten? Wenn ich einen Varchar hätte, wollte ich eine genaue Übereinstimmung, aber möglicherweise könnte es keine geben?
Piotr Kula
2
Die akzeptierte Antwort behebt dies vollständig, aber FirstOrDefault stoppt im Wesentlichen, sobald eine Übereinstimmung gefunden wird. SingleOrDefault muss jedoch die gesamte Liste untersuchen, um sicherzustellen, dass genau eine Übereinstimmung vorliegt .
stuartd
12

Dies sind die bevorzugten Methoden:

var item = Items.SingleOrDefault(x => x.Id == 123);

Oder

var item = Items.Single(x => x.Id == 123);
James Hill
quelle
Danke - also gibt es in dieser Situation keine Linq-Notation und ich muss Lambdas verwenden?
Mikey Hogarth
Das ist sehr gut. Ich habe linq nicht wirklich oft benutzt, daher bin ich mir nicht sicher, ob mir diese Methoden überhaupt bekannt waren.
Lohn
1
Eine einzelne Methode überprüft, ob der Rückgabewert eindeutig ist. Wenn Ihre Sammlung also groß ist, kann dies viele Male dauern. Die erste Methode gibt nur das erste Element zurück, das dem Prädikat entspricht.
Meziantou
@wageoghe James 'Antwort verwendet kein Linq - die Methoden Single und SingleOrDefault sind Teil der IEnumerable-Implementierung.
Mikey Hogarth
12

Um jemandem das Leben zu erleichtern, die Linq-Abfrage mit Lambda-Ausdruck

(from x in Items where x.Id == 123 select x).FirstOrDefault();

führt zu einer SQL-Abfrage mit einem select top (1)darin.

Amal
quelle
9

Das lässt sich besser darauf reduzieren.

var item = Items.First(x => x.Id == 123);

Ihre Abfrage sammelt derzeit alle Ergebnisse (und möglicherweise mehrere) innerhalb der Aufzählung und nimmt dann das erste aus diesem Satz, wodurch mehr Arbeit als erforderlich erledigt wird.

Single / SingleOrDefault lohnt sich, aber nur, wenn Sie die gesamte Sammlung durchlaufen und zusätzlich zur Auswahl dieser Übereinstimmung überprüfen möchten, ob die Übereinstimmung eindeutig ist. First / FirstOrDefault nimmt nur das erste Match und geht, unabhängig davon, wie viele Duplikate tatsächlich vorhanden sind.

Chris Hannon
quelle
4

Sie können die Syntax der Erweiterungsmethode verwenden:

var item = Items.Select(x => x.Id == 123).FirstOrDefault();

Abgesehen davon bin ich mir nicht sicher, wie viel prägnanter Sie werden können, ohne vielleicht Ihre eigenen speziellen Erweiterungsmethoden "First" und "FirstOrDefault" zu schreiben.

Lohnbuch
quelle
Ich denke nicht, dass dies das beabsichtigte Verhalten ist. Diese Select-Anweisung gibt eine Sammlung von Bool zurück (eigentlich nicht, aber für die Rückgabe), eine für jedes Element von Items, die erste von dem, was als Item zurückgegeben wird, und wird einfach zu einem Bool. Verwenden Sie die Lösung von Chris Hannon
Leonardo Daga
Vielleicht meinst du Whereim Gegensatz zu Select, was bereits gesagt wurde, aber diese Antwort ist falsch. Auswahl in c # ändert die Ergebnisse in IEnumerable <bool>, so dass Sie eine boolfür das erste Element erhaltenx.Id == 123
bradlis7
2

Ich werde dir sagen, was für mich funktioniert hat:

int id = int.Parse(insertItem.OwnerTableView.DataKeyValues[insertItem.ItemIndex]["id_usuario"].ToString());

var query = user.First(x => x.id_usuario == id);
tbUsername.Text = query.username;
tbEmail.Text = query.email;
tbPassword.Text = query.password;

Meine ID ist die Zeile, die ich abfragen möchte. In diesem Fall habe ich sie von einem radGrid erhalten und dann zum Abfragen verwendet. Diese Abfrage gibt jedoch eine Zeile zurück. Anschließend können Sie die Werte, die Sie von der Abfrage erhalten haben, einem Textfeld oder etwas anderem zuweisen Ich musste diese dem Textfeld zuweisen.

G Jeny Ramirez
quelle