IQueryable<T>.Include()被忽略了。

7

我有一个父实体和一个子实体,它们之间的关系是1对M。我需要在单个SQL查询中查询包含父实体和子实体,但是Include方法在某些情况下不能正常工作。
这个可以通过JOIN正确地单个查询ParentChild表:

var r1 =
    ctx.Set<Parent>()
       .Include(p => p.Childs)
       .Where(p => p.Id == 1)
       .ToList();

一旦我在运行时创建一个匿名对象,子对象就会丢失,SQL语句只包含父对象的字段。获取子对象仍然是惰性的 - 它们还没有被加载:

var r2 =
    ctx.Set<Parent>()
       .Include(p => p.Childs)
       .Where(p => p.Id == 2)
       .Select(p => new { myParent = p})
       .ToList();

问题:

  1. 为什么会这样?
  2. 如何在我的LINQ中构建一个新的匿名对象,使得子元素不会丢失?

附注:我希望保持Parent的Childs属性为虚拟属性。


为什么需要创建匿名对象?一个解决方案是在选择之前运行查询 var r2 = ctx.Set<Parent>().Include(p => p.Childs).Where(p => p.Id == 2).ToList() /* 或者ToArray()或者其他评估查询的方法 */.Select(p => new { myParent = p}).ToList(); - nemesv
可能是重复的问题:.Include在以下查询中并没有真正包含 - quetzalcoatl
2个回答

11

这是我所知道的EF所有版本都存在的一般性问题。 EF会尽力将“包含”尽可能传递,但是当“查询形状发生变化”时,“包含”将被无法恢复地丢失。

例如,查询的“形状”发生以下更改:

  • 使用投影(选择不是整个对象,只选择某些字段或不同的对象)
  • 使用Group By或其他聚合
  • ..可能还有其他情况,我目前不记得了。

遗憾的是,我也不记得在MSDN上我是如何碰巧看到“查询形状”解释的。如果我找到它,我会在这里放一个链接。

解决方案实际上非常简单:不要在早期指定'include'部分,而是在最终结果中指定。 因此,不是在开始时设置.include(x),而是在.Select(.. => new {..,x})中手动包括'x'。 在分组期间也可以工作。

然而,这不是一个解决方案。 这是一个手动修补程序/热修补程序,不能解决任何问题。 考虑到您可能想通过某些接口公开“IQueryable<>”,因此您可能希望公开一个“基本查询”,其中已经包含了一些内容。 当然,这无法一般性地实现,因为如果接口的客户端进行投影或分组,他将丢失“包含”的内容,甚至不知道应该包含哪些内容。 对我来说,这是EF中的一个主要缺陷。

编辑:刚找到一个链接:在后续查询中包括并没有真正包括 不是MSDN,但同样好。


0

由于您正在创建一个匿名对象,因此上下文的Parent DbSet Set<Parent>()未填充任何数据,因此也没有将Children存储在上下文中。一种解决方案是将子项添加到匿名对象中,但我不确定这是否会导致它们被添加到Set<Child> DbSet中。

var r2 = ctx.Set<Parent>()
   .Include(p => p.Childs)
   .Where(p => p.Id == 2)
   .Select(p => new { myParent = p, children = p.Childs })
   .ToList();

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