用于从 LastOrDefault 父项中查找子项的实体框架 LINQ
Posted
技术标签:
【中文标题】用于从 LastOrDefault 父项中查找子项的实体框架 LINQ【英文标题】:Entity Framework LINQ for finding sub items from LastOrDefault parent 【发布时间】:2017-04-13 08:55:46 【问题描述】:我的相关对象很少,关系就像
public class Project
public List<ProjectEdition> editions;
public class ProjectEdition
public List<EditionItem> items;
public class EditionItem
我只想为每个项目从 ProjectEditions 的最后条目中获取 EditionItems
例子
Project#1 -> Edition#1 [包含少量版本项],Edition#2 [包含少量版本项]
项目#2 -> 版本#1 、版本#2 和版本#3
我需要的输出仅包含 Project#1 的 Edition#2 和 Project#2 的 Edition#3 中的 EditionItems 。我的意思是来自项目的最新版本或项目的最后版本的 EditionItems
为了得到这个,我尝试了这个查询
List<EditionItem> master_list = context.Projects.Select(x => x.ProjectEditions.LastOrDefault())
.SelectMany(x => x.EditionItems).ToList();
但它在 LatsOrDefault() 部分返回错误
EntityFramework.SqlServer.dll 中出现“System.NotSupportedException”类型的异常,但未在用户代码中处理
附加信息:LINQ to Entities 无法识别方法 '---------.Models.ProjectEdition LastOrDefault[ProjectEdition](System.Collections.Generic.IEnumerable`1
那么我如何过滤项目的最后一个版本,然后在单个 LINQ 调用中从中获取 EditionItems 列表
【问题讨论】:
倒序排列得到FirstOrDefault()
正如您的例外所说,您的提供商不支持LastOrDefault()
。如果您考虑一下,当您使用 SQL 时,如果没有应用排序顺序,“last”这个术语就没有任何意义,因为记录可以来自服务器而没有特定的顺序。
那我怎样才能达到同样的效果呢?我正在使用 SQL Server 实体框架模型
您必须选择通过ProjectEdition
中的哪个属性来订购实体。假设那里有一个属性Id
,那么你可以使用List<EditionItem> master_list = context.Projects.Select(x => x.ProjectEditions.OrderByDescending(pe => pe.Id).FirstOrDefault()).SelectMany(x => x.EditionItems).ToList();
@granit 确实如此。你应该把它作为一个答案。
【参考方案1】:
Granit 的答案是正确的,所以我不会重复他的代码。我想补充一下这种行为的原因。
实体框架很神奇(有时太神奇了),但它将您的 LINQ 查询转换为 SQL,并且您的底层数据库可以执行的操作存在限制(本例中为 SQL Server)。
当您调用context.Projects.FirstOrDefault()
时,它会被翻译成类似Select TOP 1 * from Projects
的东西。请注意TOP 1
部分 - 这是限制返回行数的 SQL Server 运算符。这是 SQL Server 中查询优化的一部分。 SQL Server 没有任何运算符可以为您提供LAST 1
- 因为它需要运行查询,返回所有结果,取最后一个并转储其余的 - 这不是很有效,想想一个有几个表(bi) 百万条记录。
因此,您需要对查询应用任何所需的排序顺序并限制返回的行数。如果您需要查询中的最后一条记录 - 应用反向排序顺序。您确实需要排序,因为 SQL Server does not guarantee order of records returned 如果没有 Order By
应用于查询 - 这是由于数据在内部存储的方式。
当您使用 EF 编写 LINQ 查询时,我建议您密切关注查询生成的 SQL - 有时您会看到它们的复杂程度,并且您可以轻松地简化查询。有时在启用延迟加载的情况下,您只需按一下键(字面意思)就会引入 N+1 问题。我使用ExpressProfiler 观看生成的 SQL,LinqPad 也可以显示 SQL 查询,还有其他工具。
【讨论】:
有用的解释!【参考方案2】:您不能使用LastOrDefault()
或Last()
所讨论的方法here。
Insetad,您可以将OrderByDescending()
与FirstOrDefault()
结合使用,但首先您需要在ProjectEdition
中有一个属性,您要使用它来订购实体。例如。如果ProjectEdition
有一个属性Id
(很有可能),您可以使用以下LINQ 查询:
List<EditionItem> master_list = context.Projects.Select(
x => x.ProjectEditions
.OrderByDescending(pe => pe.Id)
.FirstOrDefault())
.SelectMany(x => x.EditionItems).ToList();
【讨论】:
【参考方案3】:List<EditionItem> master_list = context.Projects
.Select(p => p.editions.LastOrDefault())
.SelectMany(pe => pe.items).ToList();
如果 LastOrDefault 不支持,您可以尝试使用 OrderByDescending
List<EditionItem> master_list = context.Projects
.Select(p => p.editions.OrderByDescending(e => e.somefield).FirstOrDefault())
.SelectMany(pe => pe.items).ToList();
【讨论】:
您可以尝试使用 OrderByDescending, p.editions.OrderByDescending().FirstOrDefault() 吗? @JibinMathew,还要检查确保您的 context.Projects 返回 IEnumerable,因此请尝试使用 context.Projects.AsEnumerable().Select()。 用有用的 cmets 投票有帮助! @trailmax,非常感谢您的解释,我已经执行了它并发布了工作查询!一旦我注意到 LastOrDefault 不支持,我就为 Orderby 添加了评论,并在稍后更新了答案! thnx :) @MukeshModhvadiya 您可能会考虑删除AsEnumerable
建议,因为将整个表加载到内存中然后执行 LINQ to Objects 查询是一个糟糕的主意,其中必须考虑延迟/急切加载等问题帐户。【参考方案4】:
from p in context.project
from e in p.projectEdition.LastOrDefault()
select new EditionItem
item1 = e.item1
请试试这个
【讨论】:
以上是关于用于从 LastOrDefault 父项中查找子项的实体框架 LINQ的主要内容,如果未能解决你的问题,请参考以下文章