LINQ To Entities non riconosce il metodo Last. Veramente?


144

In questa query:

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

Ho dovuto passare a questo per farlo funzionare

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

Non potevo nemmeno usare p.First(), per rispecchiare la prima query.

Perché ci sono limitazioni di base in ciò che altrimenti sarebbe un sistema ORM così robusto?


archivia il tuo oggetto IEnumrable in una nuova variabile, quindi restituisce variabile.last (). Funzionerà.
Ali.Rashidi,

Risposte:


220

Tale limitazione deriva dal fatto che alla fine deve tradurre quella query in SQL e SQL ha un SELECT TOP(in T-SQL) ma non un SELECT BOTTOM(niente del genere).

C'è un modo semplice per aggirarlo , basta solo ordinare in ordine decrescente e poi fare un First(), che è quello che hai fatto.

EDIT: Altri provider avranno probabilmente implementazioni diverse di SELECT TOP 1, su Oracle probabilmente sarebbe qualcosa di più simileWHERE ROWNUM = 1

MODIFICARE:

Un'altra alternativa meno efficiente - NON lo consiglio! - è fare appello .ToList()ai tuoi dati prima .Last(), che eseguiranno immediatamente l'espressione LINQ To Entities che è stata costruita fino a quel momento, e quindi il tuo .Last () funzionerà, perché a quel punto .Last()viene effettivamente eseguito nel contesto di un Invece LINQ to Objects Expression. (E come hai sottolineato, potrebbe riportare migliaia di record e sprecare un sacco di oggetti materializzanti CPU che non verranno mai utilizzati)

Ancora una volta, non consiglierei di fare questo secondo, ma aiuta a illustrare la differenza tra dove e quando viene eseguita l'espressione LINQ.


e in che modo LINQ To SQL gestisce questo scenario?
Bevacqua,

@Neil sì, lo so che posso chiamare ToList, ma preferirei non recuperare migliaia di record dal database solo per filtrarli fino a cinque record
bevacqua,

2
Se sai che la tua query restituirà piccoli risultati, la chiamata ToListnon è poi così male.
Justin Skiles,

35

Invece di Last(), prova questo:

model.OrderByDescending(o => o.Id).FirstOrDefault();

14

Sostituire Last()con un selettore LinqOrderByDescending(x => x.ID).Take(1).Single()

Qualcosa del genere funzionerebbe se preferisci farlo in Linq:

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

1
C'è qualche motivo per usare .Take (1) .Single () invece di .FirstOrDefault ()?
Tot Zam,

2
@TotZam La sostituzione valida sarebbe .First () in quel caso, poiché Single () genera un'eccezione se il conteggio degli articoli non è esattamente 1.
MEMark

0

Ancora un altro modo ottenere l'ultimo elemento senza OrderByDescending e caricare tutte le entità:

dbSet
    .Where(f => f.Id == dbSet.Max(f2 => f2.Id))
    .FirstOrDefault();

0

Questo perché LINQ to Entities (e database in generale) non supporta tutti i metodi LINQ (vedere qui per i dettagli: http://msdn.microsoft.com/en-us/library/bb738550.aspx )

Quello che ti serve qui è ordinare i tuoi dati in modo tale che "l'ultimo" record diventi "primo" e quindi puoi usare FirstOrDefault. Si noti che i database di solito non hanno concetti come "primo" e "ultimo", non è come se il record inserito più di recente fosse "ultimo" nella tabella.

Questo metodo può risolvere il tuo problema

db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();

-2

L'aggiunta di una singola funzione AsEnumerable()prima di selezionare la funzione ha funzionato per me.
Esempio:

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

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


Si consiglia di incorporare il codice di lavoro dal collegamento alla risposta. Le risposte solo link attireranno l'attenzione negativa. Fornisci una risposta completa aggiungendo il codice che hai trovato per aiutare e risolvere il problema. Questo risolve un problema di collegamenti non funzionanti a causa di errori 404 in futuro.
Studocwho,

1
aggiunto l'esempio alla mia risposta
Artem Levitin,

Il lato negativo di questa risposta è che porterà tutti i risultati prima del lato server "AsEnumerable" e quindi selezionare il primo. Questo potrebbe essere molto indesiderabile. (Ho avuto una situazione come questa in cui i risultati impiegavano più di 20 secondi a causa del fatto che i record 20k + venivano portati sul lato server, una volta spostato sul lato DB, i risultati
venivano
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.