C# 包括,选择不返回的嵌套对象

C# 包括,选择不返回的嵌套对象,c#,linq,entity-framework-core,C#,Linq,Entity Framework Core,嗨,我是LINQ和EF的新手,我正在试图理解为什么下面的代码不返回嵌套实体,即使我使用include显式地加载它们 var x = await _context.AuthorBooks.Where(ub => ub.AuthorId == authorId) .Include(ub => ub.Book) .ThenInclude (b=>

嗨,我是LINQ和EF的新手,我正在试图理解为什么下面的代码不返回嵌套实体,即使我使用include显式地加载它们

 var x = await _context.AuthorBooks.Where(ub => ub.AuthorId == authorId)
                                .Include(ub => ub.Book)
                                    .ThenInclude (b=> b.Chapters)
                                    .Select(ub => ub.Book).ToListAsync();
AuthorBooks是链接对象,我可以在其中应用过滤器,仅从特定作者中选择书籍

我试图为给定作者选择包含章节的所有书籍的列表,但上面的代码返回书籍列表,但没有嵌套章节

有什么帮助吗?

解释 您正在接触EF中确实存在的行为

问题在于EF如何处理数据加载。默认情况下,它加载对象的所有标量属性,但不加载导航属性

Include通过告诉EF还包括指定的导航属性及其所有标量属性来影响此行为

然后我们就可以选择了。当您使用它时,实际上是在提供一个要检索的固定列列表。EF将仅限于您提到的字段

var x1 = _context.Books.Select(b => b.Name).ToList();
这将导致一个只检索单个列的查询

var x2 = _context.AuthorBooks.Select(ab => ab.Book).ToList();
由于您在引用导航属性时没有在其中指定任何特定的标量属性,因此EF使用其默认行为加载导航属性的所有标量属性。查询将获取X列,其中X是Book中标量属性的数量

这同样会导致查询只检索单个列,因为您引用了特定的标量属性

解决方案 一,。反转查询,这样就不需要选择

这适用于您当前的案例,但并不适用于所有情况

 var x = await _context.Books
                        .Include(b=> b.Chapters)
                        .Where(b => b.AuthorBooks.Any(ab => ab.AuthorId == authorId))
                        .ToListAsync();
二,。检索数据后执行选择

对于您的情况,这将导致您加载AuthorBook实体,这并不理想。它可以工作,但您获取的数据比您需要的多。但是,在以下情况下,这种方法更好:1。这不是一个可行的办法

 var x = await _context.AuthorBooks
                            .Include(ub => ub.Book)
                            .ThenInclude(b=> b.Chapters)
                            .Where(ub => ub.AuthorId == authorId)
                            //Fetch the data
                            .ToList()
                            //Now transform the result
                            .Select(ub => ub.Book)
                            .ToListAsync()
三,。显式地将要选择的数据添加到

注意,您不需要Include语句。因为您显式地告诉EF它应该检索什么,所以它不需要依赖于关于它应该加载什么导航属性的隐式指令。 您可以使用元组或类,而不是任意类型。返回类型由您决定,只需确保显式引用所有需要的数据,所有引用的实体的标量属性将自动加载。 四,。在选择后添加包含

NetMage在评论中首先提到了这一点

 var x = await _context.AuthorBooks.Where(ub => ub.AuthorId == authorId)
                                .Select(ub => ub.Book)
                                .Include(b => b.Chapters)
                                .ToListAsync();
请注意,前面的“包含”不是必需的,因为后续的“选择”会覆盖它们

在我看来,选项4是最干净的解决方案

不过,如果您只对导航属性的子集感兴趣,则选项3会更好。例如,如果您只想获得字数最少的章节:

 var x = await _context.AuthorBooks.Where(ub => ub.AuthorId == authorId)
                                .Select(ub => new {
                                           Book = ub.Book,
                                           Chapters = ub.Book.Chapters.Where(c => c.WordCount > 1000)
                                        });

包括加载所有相关属性。显式选择使您可以选择加载相关属性的子集,从而减少要传输的数据量。

ThenClude方法从何而来?它似乎不是EF6.2的一部分。我相信这就是EF核心。你有没有试过把a.Includeb=>b.章节放在选择之后,而不是在选择之前包含?@NetMage啊!是的,它起作用了!那你的代码以前到底是怎么运行的?它甚至不应该编译!
 var x = await _context.AuthorBooks.Where(ub => ub.AuthorId == authorId)
                                .Select(ub => ub.Book)
                                .Include(b => b.Chapters)
                                .ToListAsync();
 var x = await _context.AuthorBooks.Where(ub => ub.AuthorId == authorId)
                                .Select(ub => new {
                                           Book = ub.Book,
                                           Chapters = ub.Book.Chapters.Where(c => c.WordCount > 1000)
                                        });