【问题标题】:Entity Framework - Include Multiple Levels of Properties实体框架 - 包括多个级别的属性
【发布时间】:2012-06-05 01:32:33
【问题描述】:

Include() 方法对于对象列表非常有效。但是,如果我需要深入两层怎么办?例如,下面的方法将返回具有此处显示的包含属性的 ApplicationServers。但是,ApplicationsWithOverrideGroup 是另一个包含其他复杂对象的容器。我也可以在该属性上执行 Include() 吗?或者我怎样才能让该属性完全加载?

就目前而言,这种方法:

public IEnumerable<ApplicationServer> GetAll()
{
    return this.Database.ApplicationServers
        .Include(x => x.ApplicationsWithOverrideGroup)                
        .Include(x => x.ApplicationWithGroupToForceInstallList)
        .Include(x => x.CustomVariableGroups)                
        .ToList();
}

将仅填充 Enabled 属性(下),而不是 Application 或 CustomVariableGroup 属性(下)。我该如何做到这一点?

public class ApplicationWithOverrideVariableGroup : EntityBase
{
    public bool Enabled { get; set; }
    public Application Application { get; set; }
    public CustomVariableGroup CustomVariableGroup { get; set; }
}

【问题讨论】:

  • 嗨,为什么我在尝试这个时得到一个异常 Expression must be a member expression:要包含一个集合,然后是一个向下一级的集合:query.Include(e =&gt; e.Level1Collection.Select(l1 =&gt; l1.Level2Collection))
  • @BobHorn,我有同样的问题。在我的情况下,嵌套深入到多层,我设法做了一个包括你指出的事情。在生成的 SQL 中,我可以看到所有列都以不同的别名返回,如 c1、c2 之类的。我的问题是,我如何从我的所有包含中形成一个嵌套的 DTO 集合:(.. 可能你可以自己拿上面的例子,因为我们正在返回所有没有任何自定义 DTO 的列(它本身就是 DTO 的集合)

标签: c# entity-framework eager-loading


【解决方案1】:

对于 EF 6

using System.Data.Entity;

query.Include(x => x.Collection.Select(y => y.Property))

确保添加 using System.Data.Entity; 以获取接受 lambda 的 Include 版本。


对于 EF Core

使用新方法ThenInclude

query.Include(x => x.Collection)
     .ThenInclude(x => x.Property);

【讨论】:

  • 我不能对 ApplicationsWithOverrideGroup 执行 Include()。它不会出现在智能感知中。
  • 啊,但您提供的链接似乎提供了答案。让我试试这个:要包含一个集合,然后包含一个向下一级的集合:query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection))。
  • 记得在 usings 中包含 System.Data.Entity。否则 Intellisense 只会为您提供方法的 Include(string path) 版本。
  • @Adeem 您需要为每个属性调用IncludeDb.States.Include(state =&gt; state.Cities.Select(city =&gt; city.Customers).Include(state =&gt; state.Cities.Select(city =&gt; city.Vendors)
  • 你可以做 Db.States.Include(s => s.Cities).ThenInclude(c => c.Customers)。诀窍是 Intellisense 不会在第二个包含属性上为您提供下拉列表,而是继续前进并输入它。它将建立和工作!这只是智能感知中的错误。注意 - 仅适用于 EF Core。
【解决方案2】:

如果我理解正确,您是在询问是否包含嵌套属性。如果是这样:

.Include(x => x.ApplicationsWithOverrideGroup.NestedProp)

.Include("ApplicationsWithOverrideGroup.NestedProp")  

.Include($"{nameof(ApplicationsWithOverrideGroup)}.{nameof(NestedProp)}")  

【讨论】:

  • 谢谢,我可以试试。我希望能够保持强类型化并避免使用字符串文字。但如果这就是它必须完成的方式......
  • 你很亲密。我可能不清楚 ApplicationsWithOverrideGroup 是一个列表。感谢您的帮助!
  • @Judo,我也有同样的问题。在我的情况下,嵌套深入到多层,我设法做了一个你指出的包含。在生成的 SQL 中,我可以看到所有列都以不同的别名返回,如 c1、c2 之类的。我的问题是,我如何从我的所有包含中形成一个嵌套的 DTO 集合:(.. 可能你可以自己拿上面的例子,因为我们正在返回所有没有任何自定义 DTO 的列(它本身就是 DTO 的集合)
  • 记得在 usings 中包含 System.Data.Entity。否则 Intellisense 只会给你Include(string path) 版本的方法。
  • 您好,如果对于 EF 核心,这确实有效。Include(x => x.ApplicationsWithOverrideGroup.NestedProp) 如果是这样,使用 Include / ThenInclude 有什么意义,因为它们都可以工作并产生相同的 SQL .我错过了什么吗?
【解决方案3】:

EF Core:使用“ThenInclude”加载多个关卡: 例如:

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

【讨论】:

  • 看起来这只是 EF Core
  • 仅供参考:VS2017 智能感知不适用于 .ThenInclude。只需按照您认为应该的方式输入它,错误突出显示就会消失。
  • 我想强调@JohnWrensby 的评论,Intellisense 有时可能需要特别长时间来处理这些 ThenInclude ,这对于新用户来说可能会很混乱。我也遇到过没有正确处理简单的 Include lambda 表达式的情况,直到您只需键入并编译它,忽略 VS 中显示的“错误”。
  • @Pac0 你拯救了我的一天。努力查看子项,但看不到。
【解决方案4】:

EFCore examples on MSDN 表明你可以用IncludeThenInclude 做一些相当复杂的事情。

这是一个很好的例子,说明你可以得到多么复杂(这是一个链式语句!):

viewModel.Instructors = await _context.Instructors

      .Include(i => i.OfficeAssignment)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Enrollments)
                .ThenInclude(i => i.Student)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Department)

      .AsNoTracking()
      .OrderBy(i => i.LastName)
      .ToListAsync();

您可以进行多次Include 调用 - 即使在ThenInclude 之后,它也会将您“重置”回顶级实体(讲师)的级别。

您甚至可以多次重复相同的“第一级”集合(CourseAssignments),然后使用单独的ThenIncludes 命令来访问不同的子实体。

请注意,您的实际查询必须标记到 IncludeThenIncludes 链的末尾。以下方法不起作用:

var query = _context.Instructors.AsQueryable();
query.Include(i => i.OfficeAssignment);

var first10Instructors = query.Take(10).ToArray();

强烈建议您设置日志记录,并确保您的查询在包含多于一两件事时不会失控。了解它的实际工作原理很重要 - 您会注意到每个单独的“包含”通常是一个新查询,以避免大量连接返回冗余数据。

如果您不打算实际编辑实体并重新保存,AsNoTracking 可以大大加快速度。


EFCore 5 对多组实体的查询发送到服务器的方式进行了一些更改。 new options for Split Queries 可以通过更少的连接使某些此类查询更加高效,但请务必了解限制 - 并启用日志记录以避免以后出现性能意外。

【讨论】:

  • 有没有办法在没有你重复的情况下同时获得 Enrollment 和 Departments 的 CourseAssignment 和 Course? (到目前为止,似乎 Api 可以使用 .ThenInclude 更深入,或者使用 .Include 回到顶层,但没有什么可以保持在同一级别?)
  • 如果您想要延迟加载,请继续关注 EF Core 2.1 blogs.msdn.microsoft.com/dotnet/2018/02/02/… 但如果您只想在同一级别加载更多内容,我认为这是设计使然。我不确定你在想什么 - 这样做不需要太多额外的东西,它大大减少了从数据库返回的内容。一个实体可能只有一两个“相同级别”的东西,但对于一个大型项目,它也可能有 50 个,显式使您的应用程序更快。
  • 这很好地解释了 Include 将关卡再次“重置”回初始关卡的概念。帮助我了解包容系统的层次结构。干杯!
【解决方案5】:

我为 Entity Framework 6(.Net Core 风格)做了一个小帮手,以一种很好的方式包含子实体。

现在在 NuGet 上:Install-Package ThenInclude.EF6

using System.Data.Entity;

var thenInclude = context.One.Include(x => x.Twoes)
    .ThenInclude(x=> x.Threes)
    .ThenInclude(x=> x.Fours)
    .ThenInclude(x=> x.Fives)
    .ThenInclude(x => x.Sixes)
    .Include(x=> x.Other)
    .ToList();

包裹是available on GitHub

【讨论】:

  • 嗨,我在运行时出现异常,无法将 IncludableQueryable 转换为 IncludableQueryable
  • 我首先使用 db,我已经修改了 tt 文件以获取我所有实体的 ObservableCollections,欢迎提供任何帮助。
  • @lenny32 关于这个扩展有什么要注意的吗?
  • 请注意,如果您导航到的属性与您导航的 DbSet 是一对一的,则不需要这样做,并且您可以链接 DbSet&lt;One&gt;().Include(x =&gt; x.Two.Three.Four.Five.Six),唯一的缺点是您是计算笛卡尔积并可能增加带宽。
【解决方案6】:

我还必须使用多个包含,并且在第 3 级我需要多个属性

(from e in context.JobCategorySet
                      where e.Id == id &&
                            e.AgencyId == agencyId
                      select e)
                      .Include(x => x.JobCategorySkillDetails)
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.DurationType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RuleType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RateType))
                      .FirstOrDefaultAsync();

这可能对某人有帮助:)

【讨论】:

  • 这个可以不用重复.Include(x =&gt; x.Shifts.Select(r =&gt; r.Rate).Select(rt =&gt; rt......
  • 这取决于你想去多深
【解决方案7】:

让我明确说明,如果您不介意使用字符串文字,您可以使用字符串重载来包含嵌套级别,而不管对应关系的多重性:

query.Include("Collection.Property")

【讨论】:

  • 这种方法有助于我弄清楚如何在 VB 中对其进行编码,因为我在谷歌搜索数小时后找不到任何地方。
  • 这对我很有用,我经常使用它!!!它甚至可以与 .SelectMany 语句结合使用:query.SelectMany(x=&gt;x.foos).Include("bar").Include("bar.docs")...
【解决方案8】:

我将为我的特定问题添加我的解决方案。我需要包含两个相同级别的集合。最终的解决方案如下所示。

var recipe = _bartendoContext.Recipes
    .Include(r => r.Ingredients)
    .ThenInclude(r => r.Ingredient)
    .Include(r => r.Ingredients)
    .ThenInclude(r => r.MeasurementQuantity)
    .FirstOrDefault(r => r.Id == recipeId);
if (recipe?.Ingredients == null) return 0m;
var abv = recipe.Ingredients.Sum(ingredient => ingredient.Ingredient.AlcoholByVolume * ingredient.MeasurementQuantity.Quantity);
return abv;

这是计算给定饮料配方的酒精百分比。如您所见,我只是将成分集合包含了两次,然后将成分和数量添加到其中。

【讨论】:

    【解决方案9】:

    我想出了一个最简单的方法。您不需要安装包 ThenInclude.EF 或者您不需要对所有嵌套导航属性使用 ThenInclude。如下图所示,EF会为你照顾好休息。 示例:

    var thenInclude = context.One.Include(x => x.Twoes.Threes.Fours.Fives.Sixes)
    .Include(x=> x.Other)
    .ToList();
    

    【讨论】:

    • 没有。你不能用集合来做到这一点。仅当所有属性都是引用时才有效。你的命名暗示了相反的情况。
    猜你喜欢
    • 1970-01-01
    • 2016-06-05
    • 1970-01-01
    • 2017-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-06
    相关资源
    最近更新 更多