实体框架:查询子实体
看来我无法从数据库中获取父级及其子级的子集。
例如...
db.Parents
.Include(p => p.Children)
.Where(p => p.Children.Any(c => c.Age >= 5))
这将返回所有有 5 岁以上孩子的“父母”,但如果我迭代“Parents.Children”集合,所有孩子都将出现(而不仅仅是 5 岁以上的孩子)。
现在这个查询对我来说确实有意义(我要求包含子项并且我已经得到了它们!),但可以想象我希望在某些情况下将 where 子句应用于子集合。
我怎样才能得到一个 IEnumerable,其中每个父母都有一个经过过滤的孩子集合(年龄> = 5)?
It seems that I can't get a parent and a subset of its children from the db.
For example...
db.Parents
.Include(p => p.Children)
.Where(p => p.Children.Any(c => c.Age >= 5))
This will return all Parents that have a child aged 5+, but if I iterate through the Parents.Children collection, all children will be present (not just those over 5 years old).
Now the query does make sense to me (I've asked to include children and I've got them!), but can imagine that I would like to have the where clause applied to the child collection in some scenarios.
How could I get an IEnumerable, in which each of the parents has a filtered collection of Children (Age>=5)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在单个数据库往返中获取具有已过滤子集合的父集合的唯一方法是使用投影。无法使用预先加载 (
Include
),因为它不支持过滤,Include
始终加载整个集合。 @Daz 显示的显式加载方式需要每个父实体进行一次往返。示例:
您可以直接使用这个匿名类型对象的集合。 (您还可以投影到您自己的命名类型中,而不是匿名投影(但不能投影到像
Parent
这样的实体中)。)EF 的上下文还将填充该对象的
Children
集合。如果您不禁用更改跟踪(例如使用AsNoTracking()
),则自动为Parent
。在这种情况下,您可以将父项投影到匿名结果类型之外(发生在内存中,没有数据库查询):parents[i].Children
将包含每个Parent< 的过滤子项。 /代码>。
编辑到问题中的最后一次编辑:
上面的代码将返回所有父母,并仅包含
Age
>= 5 的孩子,因此如果只有的孩子,则可能还具有空孩子集合的父母>年龄
< 5. 您可以使用父级的附加Where
子句来过滤掉这些子句,以仅获取具有至少一个(任何
)子级的父级年龄
>= 5:The only way to get a collection of parents with a filtered children collection in a single database roundtrip is using a projection. It is not possible to use eager loading (
Include
) because it doesn't support filtering,Include
always loads the whole collection. The explicite loading way shown by @Daz requires one roundtrip per parent entity.Example:
You can directly work with this collection of anonymous type objects. (You can also project into your own named type instead of an anonymous projection (but not into an entity like
Parent
).)EF's context will also populate the
Children
collection of theParent
automatically if you don't disable change tracking (usingAsNoTracking()
for example). In this case you can then project the parent out of the anonymous result type (happens in memory, no DB query):parents[i].Children
will contain your filtered children for eachParent
.Edit to your last Edit in the question:
The code above would return all parents and include only the children with
Age
>= 5, so potentially also parents with an empty children collection if there are only children withAge
< 5. You can filter these out using an additionalWhere
clause for the parents to get only the parents which have at least one (Any
) child withAge
>= 5:在 EF Core 5.0 中,Inclusion 方法现在支持筛选包含的实体。
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#filtered-include
In EF Core 5.0, the Include method now supports filtering of the entities included.
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#filtered-include
以您的示例为例,以下内容应该可以满足您的需要。看看此处了解更多信息。
Taking your example the following should do what you need. Take a look here for more info.
我认为父母和孩子并不适合作为独立的实体。孩子总是也可以是父母,通常孩子有两个父母(父亲和母亲),所以这不是最简单的上下文。但我假设您只有简单的 1:n 关系,如我使用的以下主从模型所示。
您需要做的是创建 左外连接< /a> (这个答案引导我走上了正确的道路)。这样的联接做起来有点棘手,但这里是代码
这将使用 EF 4.1 转换为以下 SQL 语句
请注意,对联接集合的年龄执行附加的 where 子句非常重要,而不是在 from 和 the 之间执行附加的 where 子句选择。
编辑:
如果您想要分层结果,您可以通过执行分组来转换平面列表:
请注意,我使用匿名类型来存储分层结果。当然,您也可以创建这样的特定类型
,然后将组投影到此类的实例中。如果您需要将这些结果传递给其他方法,这会变得更容易。
I think parents and child are not really well suited as separate entities. A child can always also be a parent and usually a child has two parents (a father and a mother), so it's not the simplest context. But I assume you just have a simple 1:n relationship as in the following master-slave model that I used.
What you need to do is make a left outer join (that answer has led me on the right path). Such a join is a bit tricky to do, but here's the code
This will translate to the following SQL statement with EF 4.1
Note that it is important to perform the additional where clause on the age on the joined collection and not between the from and the select.
EDIT:
IF you want a hierarchical result you can convert the flat list by performing a grouping:
Note that I used an anonymous type to store the hierarchical result. You can of course create also a specific type like this
and then project the group into instances of this class. That makes it easier if you need to pass these results to other methods.