LINQ To Entities не распознает метод Last. В самом деле?

В этом запросе:

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

Мне пришлось переключить его на это, чтобы он работал

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

Я даже не мог использовать p.First(), чтобы отразить первый запрос.

Почему существуют такие основные ограничения в том, что в противном случае такая надежная система ORM?

Ответ 1

Это ограничение сводится к тому, что в конечном итоге он должен перевести этот запрос на SQL, а SQL имеет SELECT TOP (в T-SQL), но не SELECT BOTTOM (нет такой вещи).

Существует простой способ, но просто порядок убывания, а затем выполните First(), что и было.

EDIT: Другие провайдеры, возможно, будут иметь разные реализации SELECT TOP 1, в Oracle это, вероятно, будет больше похоже на WHERE ROWNUM = 1

EDIT:

Еще одна менее эффективная альтернатива - Я НЕ рекомендую это! - вызов .ToList() для ваших данных до .Last(), который немедленно выполнит созданное выражение LINQ To Entities Expression к этой точке, а затем ваш .Last() будет работать, потому что в этот момент .Last() эффективно выполняется в контексте выражения LINQ to Objects. (И, как вы указали, это может привести к тысячам записей и ненужным нагрузкам на материализующие объекты, которые никогда не будут использоваться)

Опять же, я бы не рекомендовал делать эту секунду, но это помогает проиллюстрировать разницу между тем, где и когда выполняется выражение LINQ.

Ответ 2

Вместо Last(), попробуйте следующее:

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

Ответ 3

Заменить Last() селектором Linq OrderByDescending(x => x.ID).Take(1).Single()

Что-то вроде этого будет работать, если вы предпочитаете делать это в 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());
}

Ответ 4

Еще один способ получить последний элемент без OrderByDescending и загрузить все объекты:

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

Ответ 5

Это потому, что LINQ to Entities (и базы данных в целом) не поддерживает все методы LINQ (подробности см. Здесь: http://msdn.microsoft.com/en-us/library/bb738550.aspx).

Здесь вам нужно упорядочить данные таким образом, чтобы "последняя" запись стала "первой", а затем вы можете использовать FirstOrDefault. Обратите внимание, что базы данных обычно не имеют таких понятий, как "первый" и "последний", это не означает, что последняя добавленная запись будет "последней" в таблице.

Этот метод может решить вашу проблему

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

Ответ 6

Добавление единственной функции AsEnumerable() до того, как функция Select сработало для меня.
Пример:

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

Ссылка: https://www.codeproject.com/info/1005274/LINQ-to-Entities-does-not-recognize-the-method-Sys