【问题标题】:EntityFramework - virtual ICollection queryEntityFramework - 虚拟 ICollection 查询
【发布时间】:2023-04-01 00:07:02
【问题描述】:

我有这样的实体:

public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }

    //Foreign key for Standard
    public int StandardId { get; set; }
    public virtual Standard Standard { get; set; }
}

public class Standard
{
    public int StandardId { get; set; }
    public string StandardName { get; set; }

    public virtual ICollection<Student> Students { get; set; }
}

在我的查询中,当我写:

dbcontext.Standards.Where(standard => standard.StandardId > 6)

我可以看到已经运行的底层查询是这样的:

SELECT "Extent1"."StandardId", "Extent1"."StandardName"
FROM "dbo"."Standard" as "Extent1"
WHERE "Extent1"."StandardId" > @EntityKeyValue1;

我明白,因为它是 IQueryable,所以即使是 Where 函数中的 Expression 也已被翻译,并且实际上是在数据库中而不是在程序中运行。

由于我已将集合标记为虚拟,因此未加载学生。不,我正在尝试在另一个查询中加载它们:

var XStandard = dbcontext.Standards.Where(standard => standard.StandardId == 10);
var students = XStandard.Students.Where(student => student.StudentId > 5);

我看到执行的查询是:

SELECT "Extent1"."StudentId", "Extent1"."StudentName", "Extent1"."StandardId"
FROM "dbo"."Student" as "Extent1"
WHERE "Extent1"."StandardId = @EntityKeyValue1;

我在该查询中没有看到任何 WHERE 子句检查学生 ID 是否大于 5。它实际上是在获取所有学生并在内存中过滤掉。我知道这是因为它是 IEnumerable 而不是 IQueryable,并且由于 IEnumerable 是 LINQ-to-objects,因此过滤是在内存中完成的。

当我像这样对 dbcontext 运行查询时:

var students = dbcontext.Students.Where(student => student.StudentId > 5 && student.StandardId == 10);

现在 SQL 查询是这样的:

SELECT "Extent1"."StudentId", "Extent1"."StudentName", "Extent1"."StandardId"
FROM "dbo"."Student" as "Extent1"
WHERE "Extent1"."StandardId" = @EntityKeyValue1 AND "Extent1"."StudentId" > @EntityKeyValue2;

如果对嵌套集合的查询获取所有记录而不是少数记录,那么在一对多关系中嵌套集合的意义何在? 有没有办法确保它在数据库端运行?我错过了什么吗?请帮助我了解 EF 在内部是如何工作的。提前致谢。

【问题讨论】:

  • @CamiloTerevinto System.Data.Entity 版本 6.0.0.0
  • 您是否关闭了延迟加载?请注意,dbcontext.Standards.Where(standard =&gt; standard.StandardId &gt; 6)执行查询,只是声明它。除非您关闭了延迟加载,否则使用 dbcontext.Standards.Where(standard =&gt; standard.StandardId &gt; 6).FirstOrDefault(); 应该会加载学生集合
  • 我已明确开启延迟加载。但是当我尝试访问属性时,查询正在获取整个集合并在程序中进行过滤,而不是在数据库端进行过滤。那么如果有 500 名学生,在应用过滤器后我只想要十几个记录。

标签: c# sql entity-framework entity-framework-6


【解决方案1】:

面临同样的问题。我认为您在这里需要的是显式加载嵌套集合。

dbcontext.Entry(XStandard).Collection(c => c.Students).Query().Where(student => student.StudentId > 5).Load();

然后,Where子句中的条件应该被翻译成SQL。

https://docs.microsoft.com/en-us/ef/ef6/querying/related-data?redirectedfrom=MSDN#Applying-filters-when-explicitly-loading-related-entities

但您肯定需要检索过滤后的集合,如文档中所述:

使用 Query 方法时,通常最好关闭导航属性的延迟加载。这是因为否则整个集合可能会在执行过滤查询之前或之后通过延迟加载机制自动加载。

【讨论】:

    【解决方案2】:

    您正在使用延迟加载。 在延迟加载的情况下,相关对象(子对象)在被请求之前不会与其父对象一起自动加载。默认情况下,LINQ 支持延迟加载。 也许这会帮助你决定: Lazy Loading vs Eager Loading

    【讨论】:

    • 如果您正确理解我的问题,我想要延迟加载。当我想要集合时,我希望它在数据库端被过滤和查询,但在这种情况下,首先查询整个嵌套,然后在内存中执行过滤。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多