LINQ To Entities erkennt die Methode Last nicht. "Ja wirklich?"

144

In dieser Abfrage:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters
        .OrderBy(p => p.ServerStatus.ServerDateTime)
        .GroupBy(p => p.RawName)
        .Select(p => p.Last());
}

Ich musste es darauf umstellen, damit es funktionierte

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters
        .OrderByDescending(p => p.ServerStatus.ServerDateTime)
        .GroupBy(p => p.RawName)
        .Select(p => p.FirstOrDefault());
}

Ich konnte nicht einmal verwenden p.First(), um die erste Abfrage zu spiegeln.

Warum gibt es so grundlegende Einschränkungen bei einem ansonsten so robusten ORM-System?

bevacqua
quelle
Speichern Sie Ihr IEnumrable-Objekt in einer neuen Variablen und geben Sie dann variable.last () zurück. es wird klappen.
Ali.Rashidi

Antworten:

220

Diese Einschränkung beruht auf der Tatsache, dass diese Abfrage schließlich in SQL übersetzt werden muss und SQL eine SELECT TOP(in T-SQL), aber keine SELECT BOTTOM(keine solche) hat.

Es gibt jedoch einen einfachen Weg, einfach absteigend zu bestellen und dann ein zu machen First(), was Sie getan haben.

BEARBEITEN: Andere Anbieter haben möglicherweise andere Implementierungen von SELECT TOP 1, unter Oracle wäre es wahrscheinlich eher soWHERE ROWNUM = 1

BEARBEITEN:

Eine andere weniger effiziente Alternative - ich empfehle dies NICHT! - müssen Sie .ToList()Ihre Daten vorher aufrufen .Last(), wodurch sofort der bis zu diesem Punkt erstellte LINQ To Entities-Ausdruck ausgeführt wird, und dann funktioniert Ihre .Last (), da zu diesem Zeitpunkt die .Last()im Kontext von a effektiv ausgeführt wird LINQ to Objects Expression stattdessen. (Und wie Sie bereits betont haben, könnte dies Tausende von Datensätzen zurückbringen und eine Menge CPU-materialisierender Objekte verschwenden, die niemals verwendet werden.)

Auch hier würde ich nicht empfehlen, diese Sekunde auszuführen, aber es hilft, den Unterschied zwischen dem Ort und dem Zeitpunkt der Ausführung des LINQ-Ausdrucks zu veranschaulichen.

Neil Fenwick
quelle
und wie geht LINQ To SQL mit diesem Szenario um?
Bevacqua
@Neil ja, ich weiß, dass ich ToList aufrufen kann, aber ich möchte lieber nicht Tausende von Datensätzen aus der Datenbank abrufen, nur um sie auf fünf Datensätze herunterzufiltern
bevacqua
2
Wenn Sie wissen, dass Ihre Abfrage kleine Ergebnisse liefert, ToListist das Anrufen nicht so schlecht.
Justin Skiles
35

Last()Versuchen Sie stattdessen Folgendes:

model.OrderByDescending(o => o.Id).FirstOrDefault();
Azarsa
quelle
14

Last()Durch einen Linq-Selektor ersetzenOrderByDescending(x => x.ID).Take(1).Single()

So etwas würde funktionieren, wenn Sie es in Linq vorziehen:

public static IEnumerable<IServerOnlineCharacter> GetUpdated()
{
    var context = DataContext.GetDataContext();
    return context.ServerOnlineCharacters.OrderBy(p => p.ServerStatus.ServerDateTime).GroupBy(p => p.RawName).Select(p => p.OrderByDescending(x => x.Id).Take(1).Single());
}
Ema.H
quelle
1
Gibt es einen Grund, .Take (1) .Single () anstelle von .FirstOrDefault () zu verwenden?
Tot Zam
2
@TotZam Der gültige Ersatz wäre in diesem Fall .First (), da Single () eine Ausnahme
auslöst,
0

Noch eine andere Möglichkeit, das letzte Element ohne OrderByDescending abzurufen und alle Entitäten zu laden:

dbSet
    .Where(f => f.Id == dbSet.Max(f2 => f2.Id))
    .FirstOrDefault();
Stas Boyarincev
quelle
0

Dies liegt daran, dass LINQ to Entities (und Datenbanken im Allgemeinen) nicht alle LINQ-Methoden unterstützt (Einzelheiten finden Sie hier: http://msdn.microsoft.com/en-us/library/bb738550.aspx ).

Was Sie hier brauchen, ist, Ihre Daten so zu ordnen, dass der "letzte" Datensatz "zuerst" wird und Sie dann FirstOrDefault verwenden können. Beachten Sie, dass Datenbanken normalerweise keine Konzepte wie "first" und "last" haben. Es ist nicht so, dass der zuletzt eingefügte Datensatz "last" in der Tabelle ist.

Diese Methode kann Ihr Problem lösen

db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();
Mohammad Hassani
quelle
-2

Das Hinzufügen einer einzelnen Funktion AsEnumerable()vor der Auswahlfunktion hat bei mir funktioniert.
Beispiel:

return context.ServerOnlineCharacters
    .OrderByDescending(p => p.ServerStatus.ServerDateTime)
    .GroupBy(p => p.RawName).AsEnumerable()
    .Select(p => p.FirstOrDefault());

Ref: https://www.codeproject.com/Questions/1005274/LINQ-to-Entities-does-not-recognize-the-method-Sys

Artem Levitin
quelle
Es wird empfohlen, den Arbeitscode aus dem Link in Ihre Antwort aufzunehmen. Nur-Link-Antworten ziehen negative Aufmerksamkeit auf sich. Bitte geben Sie eine vollständige Antwort, indem Sie den Code hinzufügen, den Sie gefunden haben, um das Problem zu lösen. Dies löst das Problem, dass Links aufgrund zukünftiger 404-Fehler nicht funktionieren.
Studocwho
1
fügte das Beispiel meiner Antwort hinzu
Artem Levitin
Der Nachteil dieser Antwort ist, dass alle Ergebnisse vor der Serverseite "AsEnumerable" angezeigt werden und dann die erste ausgewählt wird. Dies könnte sehr unerwünscht sein. (Ich hatte eine Situation wie diese, in der die Ergebnisse mehr als 20 Sekunden
dauerten, da