C# Entity Framework Core 3.1 显式加载多个关系导致笛卡尔爆炸,如何拆分查询?

问题描述 投票:0回答:1

我在生产环境中遇到了扩展问题。少量记录处理良好。但体积越大,花费的时间就越长。这些课程只是为了说明这一点。

Party {
   DateTime When { get; set; }
   ICollection<Attendee> Attendees { get; set; }
}
    
Attendee {
   ICollection<Gift> Gifts { get; set; }
   ICollection<Allergy> Allergies { get; set; }
}
    
IEnumerable<Party> GetAllPartiesByDate(DateTime date) {
   var parties = Context.Parties             
                    .Include(p => p.Attendees).ThenInclude(a => a.Gifts)
                    .Include(p => p.Attendees).ThenInclude(a => a.Allergies)
                    .Where(p.When == date)
                    .ToList();
   return parties;
}

有 4 个匹配派对,每个派对有 7 名参加者,每个参加者有 3 件礼物和 2 种过敏 数据库中有 172 行,分布在 6 个表中

4 + (4*7) + (4*7*3) + (4*7*2)

EF 通过单个 SQL 查询返回 168 行,还不错。

4 * (7) * (3) * (2)

但是将每个值放大 10 倍,您将在数据库中获得 142,840

40 + (40*70) + (40*70*30) + (40*70*20)
但是,单个 EF 查询的结果集会在每个连续的一对多关系上爆炸,并且 tries 返回
40 * 70 * 30 * 20
1,680,000

当使用更现代版本的 Entity Framework Core 时,笛卡尔爆炸的解决方案是使用 AsSplitQuery

由于不幸的技术原因,我们无法更新 Entity Framework Core 3.1 以后的版本。

但是如何在 EF Core 3.1 中实现 AsSplitQuery?

c# linq entity-framework-core eager-loading entity-framework-core-3.1
1个回答
0
投票

解决方案是依赖 EF Core 上下文更改跟踪行为。 IE。在给定上下文中的后续加载中,EF 检查您是否已经拥有实体的实例,如果有则更新其内容。

更改跟踪必须开启才能正常工作。

  1. 设置不包含 include 的查询,但保留 where 子句。 将其放在
    IQueryable<Party>
  2. 触发对 DB 的 EF 检索调用。这会将派对实例加载到 c# 中。
  3. 然后触发另一个 EF 检索调用,但将包含附加到 I可查询。
  4. 对每个一对多关系重复此操作。
IEnumerable<Party> GetAllPartiesByDate(DateTime date) {

    //Keep the search condition.
    IQueryable<Party> partyQuery = Context.Parties.Where(p.When == date);

    //Triger an SQL call with ToList, this creates instances of Party.
    List<Party> parties = partyQuery.ToList();

    //Trigger a sperate SQL call including specific related data. 
    //This updates the already loaded Party instances.
    List<Party> loadGifts = partyQuery.Include(p => p.Attendees).ThenInclude(a => a.Gifts).ToList();

    //Repeat 
    List<Party> loadAllergies = partyQuery.Include(p => p.Attendees).ThenInclude(a => a.Allergies).ToList();               

    return parties;                
}
© www.soinside.com 2019 - 2024. All rights reserved.