【问题标题】:How can I load all descendant items of any particular item, or a list of items?如何加载任何特定项目的所有后代项目或项目列表?
【发布时间】:2019-10-19 14:29:25
【问题描述】:

我有一个项目的这个模型:

public class Project
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public Member ProjectOwner { get; set; }
    public List<Project> ChildProjects { get; set; }
    // ... more properties
}

我想为所有项目加载所有子项目及其ProjectOwner。在我的设置中,在我们到达“叶子”之前,后代尾巴的长度没有设定限制。

目前,我正在为Index-view 执行此操作(是的,这很糟糕!):

List<Project> projects = await db.Projects
    .Where(p => p.ParentId == null)
    .Include(o => o.ProjectOwner)

    .Include(c => c.ChildProjects)
        .ThenInclude(o => o.ProjectOwner)

    .Include(c => c.ChildProjects)
        .ThenInclude(c => c.ChildProjects)
            .ThenInclude(o => o.ProjectOwner)

    .Include(c => c.ChildProjects)
        .ThenInclude(c => c.ChildProjects)
            .ThenInclude(c => c.ChildProjects)
                .ThenInclude(c => c.ProjectOwner)

    .Include(c => c.ChildProjects)
        .ThenInclude(c => c.ChildProjects)
            .ThenInclude(c => c.ChildProjects)
                .ThenInclude(c => c.ChildProjects)
                    .ThenInclude(c => c.ProjectOwner)

    .Include(c => c.ChildProjects)
        .ThenInclude(c => c.ChildProjects)
            .ThenInclude(c => c.ChildProjects)
                .ThenInclude(c => c.ChildProjects)
                    .ThenInclude(c => c.ChildProjects)
                        .ThenInclude(c => c.ProjectOwner)

    // ... and so on ... (!)


    .ToListAsync();

如何以更有效和动态的方式加载所有内容?上述代码仅限于固定数量的级别。我不能拥有那个。

几天前我问了similar question,但无法从回复中得出任何结论。

【问题讨论】:

  • 对于 所有 项目,这很容易 - 请参阅 stackoverflow.com/questions/46160780/…。基本上var rootProjects = db.Projects.Include(p =&gt; p.ProjectOwner).Include(p =&gt; p.ChildProjects).AsEnumerable().Where(p =&gt; p.ParentId == null).ToList();
  • 显式加载绝对可以解决您使用 n 包含的问题。但是,这是有代价的。如果这棵树中有 20 个叶子,您将获得 20 个 SQL 查询。不推荐用于大型应用程序。您可以尝试使用Fixing Up 来解决该问题,其中缓存了数据库上下文对象检索到的数据,并用于填充为后续查询创建的对象的导航属性。但修复起来很棘手,需要通过检查发送到数据库的 SQL 查询来进行反复试验。
  • 我回答了类似的问题here
  • @IvanStoev 太棒了!这似乎适用于项目列表。但是只有一个怎么样?
  • 不幸的是,由于 LINQ/EF Core 中缺乏递归查询支持,因此没有简单/良好/高效的过滤集解决方案。

标签: c# recursion asp.net-core-mvc entity-framework-core


【解决方案1】:

Ivan Stoev 在 cmets 中给了我这个解决方案,用于所有根项目:

var rootProjects = db.Projects
    .Include(p => p.ProjectOwner)
    .Include(p => p.ChildProjects)
    .AsEnumerable()
    .Where(p => p.ParentId == null).ToList();

由于过滤集没有如此优雅的 Linq 解决方案,我只为单个项目执行此操作:

Project project = await Task.FromResult(db.Projects
    .Include(p => p.ProjectOwner)
    .Include(p => p.ChildProjects)
        .ThenInclude(o => o.ProjectOwner)
    .AsEnumerable().Where(p => p.Id == id)
    .ToList().FirstOrDefault());

【讨论】:

    【解决方案2】:

    我有同样的问题,我决定进行下一个概念验证:

    1. 创建一个帮助类来处理树,例如:FlatToTree、TreeToFlat、GetAllDescendants、GetAllAncestors 等等
    2. 使用 AsNotracking() 选项从整个表中仅选择两个字段 ID 和 ParentID。
    3. 通过帮助程序类 GetAllDescendants 或 GetAllAncestors ID 在 ram 中
    4. 然后从表中选择仅按步骤 3 中选择的 ID 的项目,跟踪选项为 var data = _db.Table.Where(r = idList.contains(r.id))
    5. 并返回data.Where(d => d.id == requestedId)

    对不起我的英语。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-04
      • 2018-11-03
      • 2021-12-18
      • 2011-11-22
      • 1970-01-01
      • 1970-01-01
      • 2011-11-02
      相关资源
      最近更新 更多