LINQ To Entities no reconoce el método Last. De Verdad?

144

En esta consulta:

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 cambiarlo a esto 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é hay limitaciones tan básicas en lo que de otro modo es un sistema ORM tan robusto?

bevacqua
fuente
almacene su objeto IEnumrable en una nueva variable, luego devuelva variable.last (). funcionará.
Ali.Rashidi

Respuestas:

220

Esa limitación se reduce al hecho de que eventualmente tiene que traducir esa consulta a SQL y SQL tiene un SELECT TOP(en T-SQL) pero no un SELECT BOTTOM(no existe).

Sin embargo, hay una manera fácil de evitarlo, solo ordena descender y luego haz un First(), que es lo que hiciste.

EDITAR: Otros proveedores posiblemente tendrán implementaciones diferentes de SELECT TOP 1, en Oracle probablemente sería algo más comoWHERE ROWNUM = 1

EDITAR:

Otra alternativa menos eficiente: ¡NO lo recomiendo! - es llamar .ToList()a sus datos antes .Last(), lo que ejecutará inmediatamente la expresión LINQ To Entities que se ha construido hasta ese punto, y luego su .Last () funcionará, porque en ese punto .Last()se ejecuta efectivamente en el contexto de un LINQ to Objects Expression en su lugar. (Y como señaló, podría recuperar miles de registros y desperdiciar cargas de objetos de materialización de CPU que nunca se usarán)

Nuevamente, no recomendaría hacer este segundo, pero ayuda a ilustrar la diferencia entre dónde y cuándo se ejecuta la expresión LINQ.

Neil Fenwick
fuente
¿Y cómo trata LINQ To SQL con este escenario?
bevacqua
@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
2
Si sabe que su consulta arrojará pequeños resultados, llamar ToListno es tan malo.
Justin Skiles
35

En lugar de Last(), intente esto:

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

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

Algo así funcionaría si lo prefieres hacerlo 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());
}
Ema.H
fuente
1
¿Hay alguna razón para usar .Take (1) .Single () en lugar de .FirstOrDefault ()?
Tot Zam
2
@TotZam El reemplazo válido sería .First () en ese caso, ya que Single () arroja una excepción si el recuento de elementos no es exactamente 1.
MEMark
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();
Stas Boyarincev
fuente
0

Esto se debe a que LINQ to Entities (y las bases de datos en general) no es compatible con todos los métodos LINQ (consulte aquí para más detalles: http://msdn.microsoft.com/en-us/library/bb738550.aspx )

Lo que necesita aquí es ordenar sus datos de tal manera que el "último" registro se convierta en "primero" y luego pueda usar FirstOrDefault. Tenga en cuenta que databasese generalmente no tiene conceptos como "primero" y "último", no es como si el registro insertado más recientemente fuera "último" en la tabla.

Este método puede resolver tu problema

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

Agregar una sola función AsEnumerable()antes de Seleccionar me funcionó.
Ejemplo:

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
fuente
Se recomienda incorporar el código de trabajo del enlace a su respuesta. Las respuestas de solo enlace atraerán atención negativa. Proporcione una respuesta completa agregando el código que encontró para ayudar a resolver el problema. Esto resuelve un problema de enlaces que no funcionan debido a errores 404 en el futuro.
Studocwho
1
agregó el ejemplo a mi respuesta
Artem Levitin el
La desventaja de esta respuesta es que traerá todos los resultados antes del lado del servidor "AsEnumerable" y luego seleccionará el primero. Esto podría ser muy indeseable. (Tuve una situación como esta en la que los resultados demoraban más de 20 segundos debido a que los registros de más de 20k fueron traídos del lado del servidor, una vez que lo moví de regreso al lado de la base de datos, los resultados regresaron en menos de un segundo)
TChadwick