【问题标题】:LINQ To Entities does not recognize the method Last. Really?LINQ To Entities 无法识别方法 Last。真的吗?
【发布时间】:2011-11-09 18:06:07
【问题描述】:

在这个查询中:

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()。它会起作用的。

标签: c# entity-framework orm


【解决方案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 多秒,一旦我将它移回数据库端,结果会在不到一秒的时间内返回)
      猜你喜欢
      • 2023-04-01
      • 1970-01-01
      • 2014-08-09
      • 1970-01-01
      • 2012-04-03
      • 2021-11-06
      相关资源
      最近更新 更多