简体   繁体   English

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

[英]Include, select nested objects not returning

Hi Im new to LINQ and EF and im trying to understand why the below code doesn't return the nested entities even though I explicitly load them using the include 嗨,我是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 is the linking object and where I can apply the filter to select only books from the specific author. AuthorBooks是链接对象,在这里我可以应用过滤器以仅从特定作者中选择书籍。

I am trying to select the list of all Books with chapters for the given author but the above code returns the list of books but without the nested Chapters. 我正在尝试为给定作者选择带有章节的所有书籍的列表,但是上面的代码返回了没有嵌套章节的书籍列表。

Any help? 有什么帮助吗?

Explanation 说明

You're touching on a behavior that does exist in EF. 您正在触及EF中确实存在的行为。

The problem lies in how EF handles the loading of data. 问题在于EF如何处理数据加载。 By default, it loads all scalar properties of an object, but not the navigational properties. 默认情况下,它加载对象的所有标量属性,但不加载导航属性。

Include influences this behavior, by telling EF to also include a specified navigational property (with all of its scalar properties) Include通过告诉EF 包括指定的导航属性(及其所有标量属性)来影响此行为。

But then we get to Select . 但是随后我们进入Select When you use this, you are essentially giving a fixed list of columns that you want to retrieve. 使用此功能时,实际上是在提供要检索的固定列列表。 EF will limit itself to the fields that you mention. EF会将自己限制在您提到的字段中。

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

This will result in a query that only retrieves a single column. 这将导致查询仅检索单个列。

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

Since you're referring to a navigational property (without specifying any particular scalar property inside), EF uses its default behavior of loading all of the navigational property's scalar properties. 由于您指的是导航属性(在内部未指定任何特定的标量属性),因此EF使用其默认行为加载所有导航属性的标量属性。 The query will fetch X columns (where X is the amount of scalar properties in Book ). 该查询将获取X列(其中X是Book中标量属性的数量)。

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

This again will result in a query that only retrieves a single column, since you reference a specific scalar property. 这再次将导致查询仅检索单个列,因为您引用了特定的标量属性。


Solution

1. Invert the query so you don't need Select . 1.反转查询,因此您不需要Select

This works for your current case, but won't work for everything. 这适用于您当前的情况,但不适用于所有情况。

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

2. Perform the Select after retrieving the data. 2.检索数据后执行Select

For your case, this will cause you to load the AuthorBook entities, which is not ideal. 对于您的情况,这将导致您加载AuthorBook实体,这是不理想的。 It works, but you're fetching more data than you need. 它可以工作,但是您获取的数据超出了需要。 However, this approach is better in cases where 1. is not a viable approach 但是,这种方法在以下情况下更好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. Explicitly add the data you want to the Select 3.明确将所需数据添加到“ Select

 var x = await _context.AuthorBooks.Where(ub => ub.AuthorId == authorId)
                                .Select(ub => new {
                                           Book = ub.Book,
                                           Chapters = ub.Book.Chapters
                                        });
  • Notice how you don't need the Include statements. 请注意,您不需要Include语句。 Since you're explicitly telling EF what it should retrieve, it doesn't need to rely on implicit instructions about what navigational properties it should load. 由于您是在明确告知EF应该检索的内容,因此它不需要依赖隐式指令来了解应加载的导航属性。
  • Instead of an anynomous type, you could use a tuple or a class. 可以使用元组或类来代替任意类型。 The return type is up to you to decide, just make sure that you explicitly reference all the data you want (scalar properties will be loaded automatically for all referenced entities). 返回类型由您决定,只需确保您明确引用了所有需要的数据(标量属性将自动为所有引用的实体加载)。

4. Add an Include after the Select . 4.在“ Select之后添加一个“ Include ”。

Credits go to NetMage for mentioning it first in the comments. 感谢NetMage在评论中首先提到它。

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

Note that the earlier includes are not necessary since the subsequent Select overrides them anyway. 注意,前面的包含不是必需的,因为后续的Select仍然会覆盖它们。


Option 4 is the cleanest solution, in my opinion. 我认为选项4是最干净的解决方案。

However, option 3 is better if you're only interested in a subset of the navigational properties. 但是,如果您只对导航属性的子集感兴趣,那么选项3更好。 For example, if you only want to get chapters with a minimum word count: 例如,如果您只想获得最少字数的章节:

 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 loads all related properties. Include加载所有相关属性。 An explicit Select give you the option of loading a subset of related properties, thus lowering the amount of data to transfer. 显式Select使您可以选择加载相关属性的子集,从而减少要传输的数据量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM