LINQ To Entities 无法识别方法 Last。真的吗?

Posted

技术标签:

【中文标题】LINQ To Entities 无法识别方法 Last。真的吗?【英文标题】:LINQ To Entities does not recognize the method Last. Really? 【发布时间】:2011-09-03 14:14:45 【问题描述】:

在这个查询中:

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 对象存储到一个新变量中,然后返回 variable.last()。它会起作用的。 【参考方案1】:

这种限制归结为这样一个事实,即最终它必须将该查询转换为 SQL 并且 SQL 有一个 SELECT TOP(在 T-SQL 中)但没有一个 SELECT BOTTOM(没有这样的东西)。

不过有一个简单的方法,只需降序,然后执行First(),这就是您所做的。

编辑: 其他提供者可能会有不同的SELECT TOP 1 实现,在Oracle 上可能更像WHERE ROWNUM = 1

编辑:

另一个效率较低的替代方案 - 我不推荐这样做! - 是在 .Last() 之前对您的数据调用 .ToList(),这将立即执行已构建的 LINQ To Entities 表达式到那时,您的 .Last() 将起作用,因为此时 .Last()LINQ to Objects 表达式的上下文中有效执行。 (正如您所指出的,它可能会带回数千条记录并浪费大量 CPU 物化对象,这些对象永远不会被使用)

再次,我不建议这样做,但它确实有助于说明执行 LINQ 表达式的位置和时间之间的区别。

【讨论】:

LINQ To SQL 如何处理这种情况? @Neil 是的,我知道我可以调用 ToList,但我宁愿不从数据库中检索数千条记录,只是为了将它们过滤到 5 条记录 如果您知道您的查询将返回小结果,那么调用 ToList 也不错。【参考方案2】:

代替Last(),试试这个:

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

【讨论】:

如果我使用的是泛型模型并且无法引用其中的Id 字段怎么办? 如果你的数据没有排序那么Last()没有意义 @Schiavini 它仍然可能发生。我有一个数据库,我需要查询中的第一个和最后一个条目,但它们不能按任何字段排序,因此可以保证反转。【参考方案3】:

用 Linq 选择器 OrderByDescending(x =&gt; x.ID).Take(1).Single() 替换 Last()

如果你更喜欢在 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());

【讨论】:

有什么理由使用 .Take(1).Single() 而不是 .FirstOrDefault()? @TotZam 在这种情况下,有效的替换将是 .First(),因为如果项目计数不完全为 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】:

在 Select 函数对我有用之前添加一个函数 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错误导致链接失效的问题。 将示例添加到我的答案中 这个答案的缺点是它会将所有结果带到“AsEnumerable”服务器端之前,然后选择第一个。这可能是非常不可取的。 (我遇到过这样的情况,由于 20k+ 记录被带到服务器端,结果需要 20+ 秒,一旦我将它移回数据库端,结果会在不到一秒的时间内返回)

以上是关于LINQ To Entities 无法识别方法 Last。真的吗?的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to Entities 无法识别方法 IsNullOrWhiteSpace

错误:LINQ to Entities 无法识别方法 DataLength

LINQ To Entities 无法识别方法 Last。真的吗?

LINQ To Entities 无法识别方法 Last。真的吗?

C# LINQ to Entities 无法识别方法“布尔”

我如何修复“LINQ to Entities无法识别方法”错误