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

144

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

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?

bevacqua
источник
сохраните ваш объект IEnumrable в новую переменную, а затем верните variable.last (). это сработает.
Али.Рашиди

Ответы:

220

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

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

РЕДАКТИРОВАТЬ: Другие поставщики, возможно, будут иметь различные реализации SELECT TOP 1, на Oracle это, вероятно, будет что-то вродеWHERE ROWNUM = 1

РЕДАКТИРОВАТЬ:

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

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

Нил Фенвик
источник
и как LINQ To SQL справляется с этим сценарием?
bevacqua
@Neil, да, я знаю, что могу вызвать ToList, но я бы предпочел не извлекать тысячи записей из базы данных, чтобы отфильтровать их до пяти записей
bevacqua
2
Если вы знаете, что ваш запрос будет давать небольшие результаты, вызов ToListне так уж и плох.
Джастин Скилз,
35

Вместо этого Last()попробуйте это:

model.OrderByDescending(o => o.Id).FirstOrDefault();
Azarsa
источник
14

Заменить Last()селектором LinqOrderByDescending(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());
}
Ema.H
источник
1
Есть ли причина использовать .Take (1) .Single () вместо .FirstOrDefault ()?
Тот Зам,
2
@TotZam В этом случае допустимой заменой будет .First (), так как Single () выдает исключение, если количество элементов не точно равно 1.
MEMark
0

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

dbSet
    .Where(f => f.Id == dbSet.Max(f2 => f2.Id))
    .FirstOrDefault();
Стас Бояринцев
источник
0

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

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

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

db.databaseTable.OrderByDescending(obj => obj.Id).FirstOrDefault();
Мохаммед Хассани
источник
-2

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

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

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

Артем Левитин
источник
Рекомендуется включить рабочий код по ссылке в ваш ответ. Ответы только на ссылки привлекут негативное внимание. Пожалуйста, предоставьте полный ответ, добавив код, который вы нашли, чтобы помочь и решить проблему. Это решает проблему неработающих ссылок из-за 404 ошибок в будущем.
Стадион
1
добавил пример в мой ответ
Артем Левитин
Недостатком этого ответа является то, что он принесет все результаты до серверной части «AsEnumerable», а затем выберет первый. Это может быть очень нежелательно. (У меня была такая ситуация, когда результаты занимали более 20 секунд из-за того, что записи на 20 тыс .+ доставлялись на сервер, после того, как я переместил их обратно на сторону БД, результаты вернулись менее чем за секунду)
TChadwick