Entity Framework Core 忽略 .Include(..) 而没有 .ToList(..) 间接

Posted

技术标签:

【中文标题】Entity Framework Core 忽略 .Include(..) 而没有 .ToList(..) 间接【英文标题】:Entity Framework Core ignoring .Include(..) without .ToList(..) indirectly 【发布时间】:2017-09-18 09:16:36 【问题描述】:

如in "Loading Related Data" from EF Core Documentation 所述,我们可以使用.Include(..)DbSet (或通用IQueryable<T> 链接回EF 上下文)急切加载导航属性。

这意味着,给定以下模型:

public class TestEntityA

    public int Id  get; set; 
    public int TestEntityBId  get; set; 
    public TestEntityB TestEntityB  get; set; 

    public string BProperty  get  return TestEntityB.Property;  


public class TestEntityB

    public int Id  get; set; 
    public string Property  get; set; 

.. 那么下面的代码应该可以工作:

context.TestEntityAs
    .Include(m => m.TestEntityB)
    .Any(m => m.BProperty == "Hello World");
    /*
     * Note that the below DOES work by using the nav property directly
     * in the query, but that is not always going to be an option for
     * whatever reason. If it's .Included it should be available through
     * INNER JOINing it into the base query before the .Any runs.
     * .Any(m => m.TestEntityB.Property == "Hello World");
     */

其实不然。

我注意到有一个警告,如果查询不返回最初请求的类型,.Include() 可能会被忽略:

如果您更改查询以使其不再返回查询开始时使用的实体类型的实例,则包含运算符将被忽略。 [snip] 默认情况下,当忽略包含运算符时,EF Core 将记录一个警告。

我不确定在上面对.Any() 的调用中是如何相关的。是的,查询没有返回原始类型(它当然返回bool),但同时也没有记录警告以告知它被忽略。

我的问题是:

这是一个预期可行的用例吗?我应该在 EF Core 中提出错误吗? 如果不是预期的,a 解决方法如下(调用.ToList()),但这显然会加载所有内容,以查明.Any() 上是否有任何内容,这很容易被一个查询(在 EF6 中也是如此)。有什么解决方法可以让这个.Any() 在服务器端工作,从而不需要 ToList 将其放入内存?

解决方法:

context.TestEntityAs
    .Include(m => m.TestEntityB)
    .ToList()
    .Any(m => m.BProperty == "Hello World");

完整的可重复样本:https://gist.github.com/rudiv/3aa3e1bb65b86ec78ec6f5620ee236ab

【问题讨论】:

第一个不起作用的查询生成的 SQL 是什么?您不需要为此添加任何内容,它应该可以正常工作。 我想我在您的代码中看到了问题 - 您正在使用未映射的属性 m => m.BProperty。只是不要那样做 - 改用m => m.TestEntityB.Property @RudiVisser:BProperty 在您的情况下是一个计算属性,并且不映射到表列,这就是为什么它不能用于将表达式转换为 T-SQL 等。您使用的一切在IQueryable<T> 的扩展方法中必须可映射到表列,因为它会被转换为查询。 EF Core 不知道 BProperty 指向 B 属性 @Tseng 我认为 OP 的观点(我应该同意)是因为这不能被翻译成 SQL,它应该在内存中进行评估(EF Core 功能,EF6 会简单地抛出不支持例外),因此 Include 应该生效 - 就像你这样做 .AsEnumerable().Any(...) 顺便说一句有效。 @Tseng 在这种情况下我同意,但在类似context.TestEntityAs.Include(m => m.TestEntityB).Select(m => m.BProperty) 的情况下,它会更有意义。但我认为 EF 很难决定 Include 何时与最终结果相关。我认为他们不会支持这种情况。 【参考方案1】:

按照命名约定,它应该可以工作 您可以尝试使用这段代码手动配置模型之间的关系

protected override void OnModelCreating(ModelBuilder modelBuilder)

    modelBuilder.Entity<TestEntityA>()
        .HasOne(x => x.TestEntityB)
        .WithMany()
        .HasForeignKey(x => x.TestEntityBId);

【讨论】:

【参考方案2】:

该行为是预期的,但您可以使用显式加载来进行更有效的查询,如下所示。

2 个单独的查询,但不需要加载所有的 TestEntityB

// First query
var testEntityAs = context.TestEntityAs.ToList();
var testEntityAsIds = testEntityAs.Select(t => t.Id);

// Second query, can apply filter of Hello World without loading all TestEntityBs
context.TestEntityBs
    .Where(t => testEntityAsIds.Contains(t.Id) && t.Property == "Hello World")
    .Load();

// In memory check
var isAny = testEntityAs.Any(t => !string.IsNullOrEmpty(t.BProperty));

【讨论】:

【参考方案3】:

根据数据,实现这一目标的有效方法可能是加载单个记录:

context.TestEntityAs
    .Include(m => m.TestEntityB)
    .Where(m => m.BProperty == "Hello World")
    .FirstOrDefault() != null;

【讨论】:

【参考方案4】:

我不确定,但请尝试 [ForeignKey(nameof(TestEntityBId ))]

public class TestEntityA

    public int Id  get; set; 
    public int TestEntityBId  get; set; 
    [ForeignKey(nameof(TestEntityBId ))] public TestEntityB TestEntityB  get; set; 

    public string BProperty  get  return TestEntityB.Property;  


public class TestEntityB

    public int Id  get; set; 
    public string Property  get; set; 

【讨论】:

【参考方案5】:

我不确定这是否能回答您的问题,但这是一种解决方法。

BProperty 更改为getBProperty 之类的方法

public string getBProperty (TestEntityB tb)   return tb.Property;  

然后像这样写你的查询

context.TestEntityAs
    //.Include(m => m.TestEntityB) --- you don't need include anymore!
    .Any(m => m.getBProperty(m.TestEntityB)) == "Hello World");

【讨论】:

以上是关于Entity Framework Core 忽略 .Include(..) 而没有 .ToList(..) 间接的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework Core快速开始

Entity Framework Core 快速开始

EF6 System.Data.Entity.Core.EntityKey 在 Entity Framework Core 中的等价物是啥?

张高兴的 Entity Framework Core 即学即用:创建第一个 EF Core 应用

Entity Framework Core 迁移命令

Entity Framework Core - 计算字段排序