EF LINQ包含多个和嵌套实体

195

好的,我有三级实体,其层次结构如下:课程(Course) -> 模块(Module) -> 章节(Chapter)

这是原始的 EF LINQ 语句:

Course course = db.Courses
                .Include(i => i.Modules.Select(s => s.Chapters))
                .Single(x => x.Id == id); 

现在,我想添加一个名为“Lab”的实体,该实体与课程相关联。

我该如何包含这个“Lab”实体?

我尝试了以下方法,但并没有成功:

Course course = db.Courses
                .Include(i => i.Modules.Select(s => s.Chapters) && i.Lab)
                .Single(x => x.Id == id); 

有没有关于包含第二个实体的任何想法?

任何建议或信息都将不胜感激。谢谢!


1
添加另一个.Include应该可以解决问题,除非您的意思是额外的包含是Course的孙子。请参见此或者更好的选择是这个 - von v.
相关 / 可能是重复的 https://dev59.com/qHA75IYBdhLWcg3wVXWv - StuartLC
7个回答

272
你尝试过添加另一个Include吗?
Course course = db.Courses
                .Include(i => i.Modules.Select(s => s.Chapters))
                .Include(i => i.Lab)
                .Single(x => x.Id == id);

你的解决方案失败了,因为 Include 不接受布尔运算符。
Include(i => i.Modules.Select(s => s.Chapters) &&          i.Lab)
                           ^^^                  ^             ^ 
                          list           bool operator    other list
更新 要了解更多信息,请下载LinqPad并浏览示例。我认为这是熟悉Linq和Lambda最快的方法。
首先,SelectInclude之间的区别在于,使用Select您可以决定要返回(即投影)的内容。而Include是一种急切加载函数,它告诉Entity Framework您希望它包含来自其他表的数据。
Include语法也可以是字符串。像这样:
           db.Courses
            .Include("Module.Chapter")
            .Include("Lab")
            .Single(x => x.Id == id);

但是在LinqPad中的示例可以更好地解释这一点。


感谢!我在哪里可以学到更多关于这个的知识?我特别想了解Include和Select之间的区别。 - AnimaSola
3
只有这个对我起作用:.Include("Module.Chapter")。你有什么想法为什么会是这样吗? - Jo Smo
5
@JoSmo 您需要导入命名空间 System.Data.Entity 才能访问扩展方法。更多信息请参见此处 - Jens Kloster
使用System.Data.Entity; 它做到了。谢谢! - Jo Smo
你可能需要禁用延迟加载以避免无限循环错误:dbContext.Configuration.LazyLoadingEnabled = false; - Sean
1
因为提到了出色的 LinqPad 并且给出了使用 System.Data.Entity 的提示,所以我点了赞。谢谢 Jens。 - Mike

104

在实体框架核心 (EF.Core) 中,您可以使用 .ThenInclude 来包含下一层级。

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
    .ToList();

更多信息: https://learn.microsoft.com/en-us/ef/core/querying/related-data

注意: 如果你需要在blog.Posts上使用多个ThenInclude(),只需重复Include(blog => blog.Posts)并再次使用ThenInclude(post => post.Other)

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Other)
 .ToList();

在EF.Core中,我似乎无法执行".Include(i => i.Modules.Select(s => s.Chapters))",特别是在.Include内部的.Select。有人能够确认或者解释一下吗? - ttugates
@ttugates,您打算如何使用此选择?我认为您想要做的正是在EF Core中使用ThenInclude所做的。也许可以提供一个好的示例问题,这样我们就可以回答它了。 - Nick N.
@Nick N - Entity Framework Linq查询:如何在多个导航属性上进行Where并从第三个导航属性中选择。因为我选择的内容与我匹配的内容不同,所以包含关系不是必要的,因此问题是次要的。我的问题可能太“狭窄”,但感谢任何帮助。 - ttugates
1
啊,实际上,.ThenInclude() 是有效的。只是智能感知需要很长时间才能显示相关的表格。 - Chris J

25

您也可以尝试

db.Courses.Include("Modules.Chapters").Single(c => c.Id == id);

4
谢谢 - 字符串中的点表示法非常有用。 - Evert
2
这可能很有用,但是不使用它的一个原因是以后重构的容易程度:如果您在某个时候重命名“章节”实体,则其他示例将自动重命名。另一个原因是错误会更早被发现:在编译时而不是运行时。 - MGOwen
3
@MGOwen 我同意你的评论。然而,我们也可以使用:db.Courses.Include($"{nameof(Modules)}.{nameof(Chapters)}").Single(c => c.Id == id); - Zze

24

Include 是流畅接口的一部分,因此您可以编写多个Include语句,每个语句紧随其后。

 db.Courses.Include(i => i.Modules.Select(s => s.Chapters))
           .Include(i => i.Lab)
           .Single(x => x.Id == id); 

感谢您!您能告诉我在哪里可以学到更多关于这个的知识吗?谢谢! - AnimaSola
1
你知道如果模块有多个表需要连接的语法是什么吗?比如它链接到章节和其他内容? - David Spence
流利是 .Net 的一部分还是需要安装的库? - codea

6

可以这样编写扩展方法:

    /// <summary>
    /// Includes an array of navigation properties for the specified query 
    /// </summary>
    /// <typeparam name="T">The type of the entity</typeparam>
    /// <param name="query">The query to include navigation properties for that</param>
    /// <param name="navProperties">The array of navigation properties to include</param>
    /// <returns></returns>
    public static IQueryable<T> Include<T>(this IQueryable<T> query, params string[] navProperties)
        where T : class
    {
        foreach (var navProperty in navProperties)
            query = query.Include(navProperty);

        return query;
    }

即使在通用实现中,也可以像这样使用它:

string[] includedNavigationProperties = new string[] { "NavProp1.SubNavProp", "NavProp2" };

var query = context.Set<T>()
.Include(includedNavigationProperties);

1
我尝试了你的答案,但由于与自身的无限循环而抛出stackoverflowexceptions。 - Victoria S.
1
@VictoriaS.,您可以重命名扩展方法,以便它不会干扰真正的“Include”。 - Mohsen Afshin

4

这是来自我的项目

 var saleHeadBranch = await _context.SaleHeadBranch
 .Include(d => d.SaleDetailBranch)
 .ThenInclude(d => d.Item)
 .Where(d => d.BranchId == loginTkn.branchId)
 .FirstOrDefaultAsync(d => d.Id == id);

变量销售总部分支机构等于 await _context.SaleHeadBranch .Include(d => d.SaleDetailBranch) .Include("SaleDetailBranch.Item") .Where(d => d.BranchId == loginTkn.branchId) .FirstOrDefaultAsync(d => d.Id == id); - Rameesh Puthukkudy

0

这是我的工作示例。 AccountInfoes 是 User 的父级, User 是 Currency 和 Country 的父级。

var accountinfo =  _context.AccountInfoes.Include(a => a.User)
                         .Include(a=>a.User.Currency)
                       .Include(a => a.User.Country)
            .FirstOrDefaultAsync(ai => ai.UserId == userId);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接