2011-09-03 11 views
111

In questa query:LINQ alle entità non riconosce il metodo Ultimo. Veramente?

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 cambiare 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 ho potuto anche utilizzare p.First(), per rispecchiare la prima query.

Perché esistono dei limiti di base in un sistema ORM così robusto?

+0

memorizzare l'oggetto IEnumrable in una nuova variabile, quindi restituire variable.last(). Funzionerà. –

risposta

174

Tale limitazione si riduce al fatto che alla fine si deve tradurre la query per SQL e SQL ha un SELECT TOP (in T-SQL), ma non un (nulla di simile) SELECT BOTTOM.

C'è un modo semplice intorno ad esso, però, solo ordine decrescente e poi fare un First(), che è quello che hai fatto.

EDIT: Altri fornitori possibilmente avrà diverse implementazioni di SELECT TOP 1, su Oracle sarebbe probabilmente qualcosa di più simile WHERE ROWNUM = 1

EDIT:

Un altro meno efficiente alternativa - io non lo faccio consiglia questo! - chiama il numero .ToList() prima del .Last(), che eseguirà immediatamente LINQ su Espressione entità che è stata creata fino a quel momento, quindi il tuo .Last() funzionerà, perché a quel punto lo .Last() viene effettivamente eseguito in il contesto di LINQ to Objects Espressione. (E come hai sottolineato, potrebbe riportare indietro migliaia di record e sprecare un sacco di CPU che materializzano oggetti che non saranno mai usati)

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

+0

e in che modo LINQ To SQL gestisce questo scenario? – bevacqua

+1

@Nico: getta pure. – jason

+0

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

12

Sostituire Last() da un selettore Linq OrderByDescending(x => x.ID).Take(1).Single()

Qualcosa del genere sarebbe opere se prefert fate 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()); 
} 
+0

Questo ha funzionato perfettamente per me. –

+1

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

+1

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

17

Invece di Last(), Prova questo:

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

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

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