实体框架核心忽略.Include(..)而没有.ToList(..)间接

时间:2017-04-21 11:25:47

标签: c# entity-framework asp.net-core .net-core entity-framework-core

如上所述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会在忽略include运算符时记录警告。

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

我的问题是:

  • 这是一个预期有用的用例吗?我应该在EF Core中提出错误吗?
  • 如果没有预期, 解决方法如下(致电.ToList()),但这显然会加载所有内容,以确定.Any()上是否有任何内容这可能很容易成为一个查询(在EF6中也是如此)。什么是使.Any()在服务器端工作的解决方法,因此不需要ToList将其放入内存?

解决方法:

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

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

3 个答案:

答案 0 :(得分:0)

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

2个单独的查询,但无需加载所有TestEntityBs

// 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));

答案 1 :(得分:0)

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

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<TestEntityA>()
        .HasOne(x => x.TestEntityB)
        .WithMany()
        .HasForeignKey(x => x.TestEntityBId);
}

答案 2 :(得分:0)

我不确定,但试试 [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; }
}