3 Stimmen

Wäre diese SingleOrDefault()-Optimierung lohnenswert oder ist sie übertrieben / schädlich?

Ich habe mit LinqToSQL und LINQPad herumgespielt und bemerkt, dass SingleOrDefault() in der generierten SQL keine Filterung oder Begrenzung vornimmt (ich hatte fast das Äquivalent von Take(1) erwartet).

Angenommen, Sie wollten sich davor schützen, dass große Mengen versehentlich zurückgeschickt werden, wäre dann das folgende Snippet nützlich oder eine schlechte Idee?

// SingleType is my LinqToSQL generated type 
// Singles is the table that contains many SingleType's
// context is my datacontext
public SingleType getSingle(int id)
{
     var query = from s in context.Singles where s.ID == id select s;
     var result = query.Take(2).SingleOrDefault();
     return result;
}

Im Gegensatz zu dem normalen Weg, den ich gewählt hätte (beachten Sie, dass .Take(2) fehlt)

public SingleType getSingle(int id)
{
     var query = from s in Singles where s.ID == id select s;
     var result = query.SingleOrDefault();
     return result;
}

Ich dachte, mit der Take(2), ich würde immer noch die Funktionalität von SingleOrDefault() mit dem zusätzlichen Vorteil der nie zu kümmern, über die Rückkehr {n} Zeilen versehentlich, aber ich bin nicht sicher, wenn seine sogar wert, es sei denn, ich bin ständig erwarten, um versehentlich {n} Zeilen mit meiner Abfrage zurück.

Lohnt sich das also? Ist es schädlich? Gibt es irgendwelche Vor- oder Nachteile, die ich nicht sehe?

Edit :

SQL generiert ohne den Take(2)

SELECT [t0].[blah], (...) 
FROM [dbo].[Single] AS [t0]
WHERE [t0].[ID] = @p0

Mit Take(2) erzeugtes SQL

SELECT TOP 2 [t0].[blah], (...) 
FROM [dbo].[Single] AS [t0]
WHERE [t0].[ID] = @p0

Auch, wenn ich spreche von SingleOrDefault-Funktionalität, ich speziell Wunsch zu haben, es werfen eine Ausnahme, wenn 2 oder mehr zurückgegeben werden, weshalb ich eine "Take(2)" tun bin. Der Unterschied ist, ohne die .Take(2), wird es {n} Zeilen aus der Datenbank zurückgeben, wenn es wirklich nur 2 zurückgeben muss (gerade genug, um es zu werfen).

3voto

Neil Williams Punkte 11758

Single ist eher eine bequeme Methode, um das einzelne Element einer Abfrage zu erhalten, als eine Möglichkeit, die Anzahl der Ergebnisse zu begrenzen. Durch die Verwendung von Single sagen Sie im Grunde: "Ich weiß, dass diese Abfrage nur ein Element enthalten kann, also geben Sie es mir einfach", genau wie bei der Verwendung von someArray[0] wenn Sie wissen, dass es nur ein Element geben wird. SingleOrDefault fügt die Möglichkeit der Rückgabe von null anstatt eine Ausnahme zu machen, wenn es sich um Sequenzen der Länge 0 handelt. Sie sollten nicht mit Single o SingleOrDefault mit Abfragen, die mehr als 1 Ergebnis liefern können: ein InvalidOperationException geworfen werden.

Si ID in Ihrer Abfrage ist der Primärschlüssel der Tabelle, oder ein UNIQUE Spalte wird die Datenbank sicherstellen, dass die Ergebnismenge 1 Zeile oder keine Zeile enthält, ohne dass eine TOP Klausel.

Wenn Sie jedoch auf einer nicht eindeutigen / Nicht-Schlüsselspalte selektieren und das erste Ergebnis oder das letzte Ergebnis wünschen (beachten Sie, dass diese keine Bedeutung haben, es sei denn, Sie führen auch eine OrderBy ), dann können Sie First o Last (die beide über OrDefault Gegenstücke), um das gewünschte SQL zu erhalten:

var query = from s in context.Singles 
            where s.Id == id
            orderby s.someOtherColumn
            select s;

var item = query.FirstOrDefault();

Nebenbei bemerkt können Sie sich einige Tipparbeit sparen, wenn Sie tatsächlich eine Abfrage für ein einzelnes Element durchführen:

var query = from s in context.Singles where s.Id == id select s;
var item = query.SingleOrDefault();

werden kann:

var item = context.Singles.SingleOrDefault(s => s.Id == id);

2voto

Reed Copsey Punkte 536986

SingleOrDefault (und die IEnumerable<T>.SingleOrDefault() ) lösen beide eine InvalidOperationException aus, wenn die Sequenz mehr als ein Element hat.

Ihr obiger Fall kann niemals eintreten - es wird eine Ausnahme ausgelöst.


Edit :

Mein Vorschlag dazu hängt von Ihrem Nutzungsszenario ab. Wenn Sie denken, dass es Fälle geben wird, in denen Sie regelmäßig mehr als ein paar Zeilen aus dieser Abfrage zurückgeben werden, dann fügen Sie die Zeile .Take(2) hinzu. Dadurch erhalten Sie das gleiche Verhalten und die gleiche Ausnahme, aber das Potenzial für die Rückgabe viele Datensätze aus der DB zu beseitigen.

Ihre Verwendung von SingleOrDefault() legt jedoch nahe, dass niemals mehr als eine Zeile zurückgegeben werden sollte. Wenn das wirklich der Fall ist, würde ich dies weglassen und nur als Ausnahme behandeln. Meiner Meinung nach verringern Sie die Lesbarkeit des Codes, indem Sie suggerieren, dass es normal wäre, >2 Datensätze zu haben, wenn Sie .Take(2) hinzufügen, und in diesem Fall glaube ich nicht, dass das wahr ist. Ich würde die Leistungseinbußen im Ausnahmefall in Kauf nehmen, weil es so einfach ist, es wegzulassen.

2voto

Daniel Brückner Punkte 57561

Sie haben diesen Punkt bereits erwähnt. Wenn Sie erwarten, dass die Abfrage häufig eine große Anzahl von Zeilen zurückgibt, könnte dies einen Leistungsgewinn bedeuten. Aber wenn dies ein Ausnahmefall ist - und SingleOrDefault() zeigt deutlich, dass sich der Aufwand nicht lohnt. Es verschmutzt nur Ihren Code und Sie sollten es dokumentieren, wenn Sie es zulassen.

UPDATE

Ich habe gerade bemerkt, dass Sie die ID abfragen. Ich nehme an, es ist der Primärschlüssel und Sie erhalten eine oder null Zeilen in Folge. Theoretisch sollten Sie sich also nicht viel aus der Verwendung von Single() , First() , Take(1) oder was auch immer. Aber ich würde es immer noch als gutes Design ansehen, wenn man Single() ausdrücklich angeben, dass Sie genau eine Zeile erwarten. Ein Kollege erzählte mir vor ein paar Wochen, dass er sogar ein Projekt hatte, bei dem etwas furchtbar schief gelaufen ist und der Primärschlüssel aufgrund einer großen Datenbankstörung nicht mehr eindeutig war. Also lieber auf Nummer sicher gehen.

0voto

Gus Cavalcanti Punkte 10321

Allen, ist ID der Primärschlüssel für die Tabelle Singles? Wenn ja, verstehe ich Ihr Problem nicht ganz, da Ihre zweite Abfrage einen Datensatz oder null zurückgibt. Und die SQL-Abfrage lautet where ID = ###... Die Verwendung von Take(2).SingleOrDefault() macht den Zweck von SingleOrDefault zunichte.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X