在 EFCore 急切加载 LINQ 查询中,如何在 ThenInclude() 表达式中引用***实体?
Posted
技术标签:
【中文标题】在 EFCore 急切加载 LINQ 查询中,如何在 ThenInclude() 表达式中引用***实体?【英文标题】:In an EFCore eager loading LINQ query, how to reference the top-level entity within the ThenInclude() expression? 【发布时间】:2021-09-17 09:33:07 【问题描述】:我有一个使用 EFCore 5 的 LINQ 查询,它急切地加载了多个级别的相关实体。在其中一个中,我需要根据***实体上的字段过滤引用的实体,有没有办法做到这一点?
我想要的查询,除了在Where
内,我如何引用product
?
context.Products
.Include(product => product.PrimaryComponent)
.ThenInclude(component => component.ComponentRules
.Where(cRule => cRule.FactoryId == product.FactoryId))
.Where( /* other filters */ )
在Where()
表达式中,我可以引用cRule
和component
,但我不能引用product
。
表格:
dbo.Product ( Id int, FactoryId int, PrimaryComponentId int )
dbo.Component ( Id int, Name nvarchar(100) )
dbo.ComponentRule ( ComponentId int, RuleId int, FactoryId int, Notes nvarchar(max) )
-- These tables aren't used in these queries but here they are fyi:
dbo.Rule ( Id int, Name nvarchar(100), ... )
dbo.Factory ( Id int, Name nvarchar(100), ... )
在这个数据库中,产品使用组件,每个组件都有许多关联的规则,这取决于我们谈论的是哪个工厂。每个产品仅在一个工厂中构建,因此当我获得 ComponentRule 对象时,我只想加载与产品的 FactoryId 相关的对象,而不是所有工厂的所有 ComponentRule。每个 Component 仍然会有几个 ComponentRules,只是没有那么多。
如果我要编写一个 SQL 查询,这就是我的想法:
select *
from dbo.Product
inner join dbo.Component
on Product.PrimaryComponentId = Component.Id
inner join dbo.ComponentRule
on Component.Id = ComponentRule.ComponentId
-- The line below is the tricky one:
and ComponentRule.FactoryId = Product.FactoryId
-- ... plus other filters
where
我不能轻易地只为它编写 SQL,因为我确实引入了其他几个实体并使用 .AsSplitQuery()
来提高效率。所以我真的很想能够从.ThenInclude(...)
中引用***Product.FactoryId
。有什么办法吗?
【问题讨论】:
查询结果会有什么用?如果您打算将其用于只读目的,则投影可能是一种替代方法。 BR 是的,只读使用。所以你的意思是像@GraphWalk 的答案,在Select()
中使用过滤?
我想到了一些更简单的东西,比如:***.com/a/52025598/14072498
谢谢,我看看能不能那样做。我实际上包含了 17 个不同的实体,所以像这样交换并不是一件容易的事。但我会尝试一下。
我只是希望有一种简单的方法可以做到这一点,但我错过了。例如在 SQL 中很容易。也许我们需要像 .ThenInclude( parent, child => ... )
这样的重载,而不仅仅是 .ThenInclude( child => ... )
【参考方案1】:
UPD:假设您的模型是:
// in your OnModelCreating()
modelBuilder.Entity<ComponentRule>()
.HasOne(p => p.Component)
.WithMany(b => b.ComponentRules);
public class Product
public int Id get; set;
public int FactoryId get; set;
public int PrimaryComponentId get; set;
public Component PrimaryComponent get; set;
public class Component
public int Id get; set;
public string Name get; set;
public List<ComponentRule> ComponentRules get; set;
public class ComponentRule
public int ComponentId get; set;
public Component Component get; set;
public int FactoryId get; set;
也许你可以这样做:
context.Products
// 'Include's are not needed for LINQ query with custom projection (thanks Svyatoslav Danyliv)
// .Include(product => product.PrimaryComponent)
// .ThenInclude(component => component.ComponentRules)
.Where( /* other filters */ )
.Select(product => new Product
Id = product.Id,
FactoryId = product.FactoryId,
PrimaryComponent = new Component
Id = product.PrimaryComponent.Id,
Name = product.PrimaryComponent.Name,
ComponentRules = product.PrimaryComponent.ComponentRules
.Where(r => r.FactoryId == product.FactoryId).ToList()
,
)
【讨论】:
一条注释,使用自定义投影的 LINQ 查询不需要包含。 谢谢,我看看能不能做到这样。实际上,我包含了 17 个不同的实体,因此交换它并非易事:) 但我会尝试尝试一下。以上是关于在 EFCore 急切加载 LINQ 查询中,如何在 ThenInclude() 表达式中引用***实体?的主要内容,如果未能解决你的问题,请参考以下文章
在 NHibernate 3.0 Linq 中急切加载多个兄弟姐妹和孙辈(堂兄弟?)的良好行为