考虑以下LINQ查询:
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select new
{
ItemProp1 = obj,
ItemProp2 = obj.NavProp2.Any(n => n.Active)
}).SingleOrDefault();
这将按预期运行,但是
item.ItemProp1.NavProp1
为NULL。正如它解释here一样,这是因为查询实际上在使用
Include()
之后发生了变化。但是问题是这种情况下的解决方案是什么?编辑:
当我这样更改查询时,一切正常:
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select obj).SingleOrDefault();
关于this article,我想是什么问题...但是作者提供的解决方案在我的情况下不起作用(因为在最终选择中使用匿名类型而不是实体类型)。
最佳答案
如您所述,Include
仅在查询的最终结果由应包含Include
-d导航属性的实体组成时才有效。
因此,在这种情况下,Include
具有作用:
var list = _db.SampleEntity.Include(s => s.NavProp1).ToList();
SQL查询将包含一个
JOIN
,并且每个SampleEntity
都将加载其NavProp1
。在这种情况下,它无效:
var list = _db.SampleEntity.Include(s => s.NavProp1)
.Select(s => new { s })
.ToList();
SQL查询甚至不包含
JOIN
,EF完全忽略了Include
。如果在后一个查询中,您希望
SampleEntity
包含其NavProp1
,则可以执行以下操作:var list = _db.SampleEntity
.Select(s => new { s, s.NavProp1 })
.ToList();
现在,Entity Framework已经从数据库中分别获取了
SampleEntity
和NavProp1
实体,但是它通过称为关系修复的过程将它们粘合在一起。如您所见,Include
对于实现此目标不是必需的。但是,如果
Navprop1
是一个集合,您会注意到...var navprop1 = list.First().s.Navprop1;
...仍会执行查询以通过延迟加载来获取
Navprop1
。这是为什么?尽管关系修正确实填充了
Navprop1
属性,但并未将其标记为已加载。仅当Include
加载属性时才会发生这种情况。因此,现在我们的SampleEntity
都具有其Navprop1
,但是如果不触发延迟加载就无法访问它们。您唯一可以防止这种情况发生的方法是_db.Configuration.LazyLoadingEnabled = false;
var navprop1 = list.First().s.Navprop1;
(或通过禁用代理创建或不将
Navprop1
设为虚拟来防止延迟加载。)现在,您无需重新查询即可获得
Navprop1
。对于引用导航属性,此功能不适用,启用时不会触发延迟加载。
在 Entity Framework核心中,该区域的情况已经发生了巨大变化。像
_db.SampleEntity.Include(s => s.NavProp1).Select(s => new { s })
这样的查询现在将在最终结果中包括NavProp1
。 EF-core在最终结果中寻找“包含”实体方面更为明智。因此,我们不会倾向于像Select(s => new { s, s.NavProp1 })
这样的查询来填充导航属性。但是请注意,如果我们使用不带Include
的查询,则当访问s.NavProp1
时仍会触发延迟加载。