2011-09-03 10 views
111

En esta consulta:LINQ To Entities no reconoce el método Last. De Verdad?

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

tuve que cambiar a este para que funcione

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

Ni siquiera podía usar p.First(), para reflejar la primera consulta.

¿Por qué existen tales limitaciones básicas en un sistema ORM tan robusto?

+0

almacena tu objeto IEnumrable en una nueva variable, luego devuelve variable.last(). funcionará. –

Respuesta

174

Esa limitación se reduce al hecho de que con el tiempo se tiene que traducir esa consulta SQL a y SQL tiene un SELECT TOP (en T-SQL), pero no una SELECT BOTTOM (tal cosa).

Hay una manera fácil de evitarlo, solo orden descendente y luego hacer un First(), que es lo que hizo.

EDIT: Otros proveedores posiblemente tienen diferentes implementaciones de SELECT TOP 1, en Oracle, probablemente sería algo más parecido a WHERE ROWNUM = 1

EDIT:

Otro menos eficiente alternativa - no lo hago recomiendo esto! - debe llamar al .ToList() en sus datos antes de .Last(), que ejecutará de inmediato la expresión LINQ To Entities que se ha generado hasta ese momento, y luego su .Last() funcionará, porque en ese momento el .Last() se ejecuta efectivamente en el contexto de LINQ to Objects Expresión en su lugar. (Y como usted señaló, podría traer miles de registros y desperdiciar un montón de objetos de materialización de CPU que nunca se usarán)

De nuevo, no recomendaría hacer esto en segundo lugar, pero ayuda a ilustrar la diferencia entre dónde y cuando se ejecuta la expresión LINQ.

+0

y cómo lidia LINQ To SQL con este escenario? – bevacqua

+1

@Nico: se tira también. – jason

+0

@Neil sí Sé que puedo llamar a ToList, pero prefiero no recuperar miles de registros de la base de datos solo para filtrarlos a cinco registros – bevacqua

12

Reemplazar Last() por un selector LINQ OrderByDescending(x => x.ID).Take(1).Single()

Algo así habría funciona si prefert lo hace en 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

Esto funcionó perfectamente para mí. –

+1

¿Hay alguna razón para usar .Take (1) .Single() en lugar de .FirstOrDefault()? –

+1

@TotZam El reemplazo válido sería .Primero() en ese caso, ya que Single() arroja una excepción si el recuento de elementos no es exactamente 1. – MEMark

17

En lugar de Last(), Prueba esto:

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

Otra forma de obtener el último elemento sin OrderByDescending y cargar todas las entidades:

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