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



嗨,我是 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还包括指定的导航属性(及其所有标量属性(来影响此行为

但随后我们到了Select.当你使用它时,你实际上是在给出一个要检索的列的固定列表。EF 会将自身限制为您提到的字段。

var x1 = _context.Books.Select(b => b.Name).ToList();

这将导致查询仅检索单个列。

var x2 = _context.AuthorBooks.Select(ab => ab.Book).ToList();

由于你引用的是导航属性(未在其中指定任何特定的标量属性(,EF 使用其默认行为加载导航属性的所有标量属性。查询将获取 X 列(其中 X 是Book中的标量属性的数量(。

var x3 = _context.AuthorBooks.Select(ab => ab.Book.Name).ToList();

这将再次导致查询仅检索单个列,因为您引用了特定的标量属性。


溶液

1. 反转查询,这样你就不需要Select了。

这适用于您当前的情况,但不适用于所有情况。

var x = await _context.Books
.Include(b=> b.Chapters)
.Where(b => b.AuthorBooks.Any(ab => ab.AuthorId == authorId))
.ToListAsync();

2. 检索数据后执行Select

对于您的情况,这将导致您加载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()

3. 将所需的数据显式添加到Select

var x = await _context.AuthorBooks.Where(ub => ub.AuthorId == authorId)
.Select(ub => new {
Book = ub.Book,
Chapters = ub.Book.Chapters
});
  • 请注意,您不需要Include语句。由于你显式告知 EF 它应检索的内容,因此它不需要依赖于有关应加载哪些导航属性的隐式说明。
  • 您可以使用元组或类,而不是任何类型。返回类型由您决定,只需确保显式引用所需的所有数据(标量属性将自动加载到所有引用的实体(。

4.在Select后添加一个Include

感谢NetMage在评论中首先提到它。

var x = await _context.AuthorBooks.Where(ub => ub.AuthorId == authorId)
.Select(ub => ub.Book)
.Include(b => b.Chapters)
.ToListAsync();

请注意,前面的包含不是必需的,因为后续Select无论如何都会覆盖它们。


在我看来,选项 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)
});

Include加载所有相关属性。显式Select为您提供加载相关属性子集的选项,从而减少要传输的数据量。

最新更新