LINQ до сутностей не розпізнає метод 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?


збережіть ваш об'єкт IEnumrable у новій змінній, а потім поверніть змінну.last (). це спрацює.
Алі.Рашиді

Відповіді:


220

Це обмеження зводиться до того, що зрештою він повинен перевести цей запит у SQL, і SQL має SELECT TOP(у T-SQL), але не SELECT BOTTOM(немає такого).

Однак є простий спосіб її подолання, просто накажіть низхідне, а потім зробіть таке First(), що і зробили.

EDIT: Інші постачальники, можливо, матимуть різні реалізації SELECT TOP 1, в Oracle це, мабуть, буде щось більшеWHERE ROWNUM = 1

Редагувати:

Ще одна менш ефективна альтернатива - Я НЕ рекомендую цього! - це закликати .ToList()до своїх даних раніше .Last(), що негайно виконає LINQ для сутностей Expression, який був побудований до цього моменту, і тоді ваш .Last () буде працювати, тому що в цьому пункті .Last()ефективно виконується в контексті Натомість LINQ для вираження об'єктів . (І як ви зазначали, це може повернути тисячі записів і відпрацьованих навантажень процесора, що матеріалізують об'єкти, які ніколи не будуть використовуватися)

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


і як LINQ To SQL справляється з цим сценарієм?
bevacqua

@Neil так, я знаю, я можу зателефонувати в ToList, але я краще не отримуватиму тисячі записів із бази даних, аби відфільтрувати їх до п'яти записів
bevacqua

2
Якщо ви знаєте, що ваш запит поверне невеликі результати, дзвінок ToListне так вже й поганий.
Джастін Скілз

35

Замість цього Last()спробуйте:

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

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

1
Чи є якась причина використовувати .Take (1) .Single () замість .FirstOrDefault ()?
Tot Zam

2
@TotZam У цьому випадку дійсною заміною буде. Перший (), оскільки Single () видає виняток, якщо кількість елементів не є точно 1.
ПАМ'ЯТЬ

0

Ще один спосіб отримати останній елемент без OrderByDescending та завантажити всі сутності:

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

0

Це тому, що LINQ для сутностей (і загалом баз даних) не підтримує всі методи LINQ (детальніше див. Тут: http://msdn.microsoft.com/en-us/library/bb738550.aspx )

Тут вам потрібно замовити свої дані таким чином, щоб "останній" запис став "першим", а потім ви можете використовувати FirstOrDefault. Зауважте, що в базі даних зазвичай немає таких понять, як "перший" і "останній", це не так, як остання вставлена ​​запис буде "останньою" в таблиці.

Цей метод може вирішити вашу проблему

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

-2

Додавання однієї функції AsEnumerable()до того, як функція Select працює для мене.
Приклад:

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 помилки в майбутньому.
Studocwho

1
додав приклад до моєї відповіді
Артем Левітін

Нижній бік цієї відповіді полягає в тому, що вона принесе всі результати перед стороною сервера "AsEnumerable", а потім вибере перший. Це може бути дуже небажаним. (У мене була така ситуація, коли результати займали 20+ секунд через те, що записи 20k + були доставлені на стороні сервера, як тільки я повернув її назад на сторону БД, результати повернулися менше ніж за секунду)
TChadwick,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.