Entity Framework Include() 在复杂查询中不起作用

Entity Framework Include() is not working within complex query

考虑以下 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.NavProp1NULL。 正如它所解释的那样 here 这是因为在使用 Include() 之后查询实际上发生了变化。但问题是这种情况的解决方案是什么?

编辑:

当我这样更改查询时,一切正常:

var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
           select obj).SingleOrDefault();

关于 this article 我猜是什么问题...但是作者提供的解决方案在我的情况下不起作用(因为在最后使用 anonymous type select 而不是 实体类型 ).

如果您的模型定义正确,它应该可以正常工作。

using System.Data.Entity;

var item = _db.SampleEntity
                   .Include(p => p.NavigationProperty)
                   .Select(p => new YourModel{
                         PropertyOne = p.Something,
                         PropertyTwo = p.NavigationProperty.Any(x => x.Active)
                    })
                   .SingleOrDefault(p => p.Something == true);

你怎么发现 item.ItemProp1.NavProp1 是空的。当您尝试访问时,EF 使用代理加载所有必需的属性。

怎么样

var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
   select obj).SingleOrDefault();

Assert.IsNotNull(obj.NavProp1);
Assert.IsNotNull(obj.NavProp2);

你也可以试试

var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
   select new
   {
        ItemProp1 = obj,
        NavProp1 = obj.NavProp1,
        ItemProp2 = obj.NavProp2.Any(n => n.Active)
   }).SingleOrDefault();

Assert.IsNotNull(item.NavProp1)

当然,我假设您对 EF 导航 属性 映射没有任何问题。

如您所述,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 已经分别从数据库中获取了 SampleEntityNavProp1 实体,但是它通过一个名为 relationship fixup[=74 的过程将它们粘合在一起=].如您所见,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 在最终结果中寻找 "Includable" 个实体时更加智能。因此,我们不会倾向于像 Select(s => new { s, s.NavProp1 }) 这样的查询来填充导航 属性。但是请注意,如果我们使用这样的查询而没有Include,当s.NavProp1时仍然会触发延迟加载已访问。

我知道这可能会引起一些笑声,但不要像我刚才那样忘记显而易见的事情。数据库中的行实际上没有外键引用!在认为 EF Include 不起作用之前,我应该先检查大坝数据!哎呀。我生命中的 30 分钟我不会回来。