EF6虚拟导航属性是否导致SQL查询?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EF6虚拟导航属性是否导致SQL查询?相关的知识,希望对你有一定的参考价值。

假设我有一个具有虚拟导航属性的实体,如下所示:

public class School
{
    public long Id { get; set; }

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

据我了解,EF6使用代理来启用Students的延迟加载,但执行以下LINQ查询:

var myStudent = this.Students.Single(x => x.Id == id);
var studentsCount = this.Students.Count();
var bestStudents = this.Students
    .OrderByDescending(x => x.GPA)
    .Take(5)
    .ToArray();

结果进入SQL查询(就像IQueryable<T>那样)?或者它只是一个延迟加载的集合,并将在第一次请求后将所有学生加载到内存中,然后执行简单的IEnumerable<T> LINQ行为?

答案

在Entity Framework中查询实体时,返回的对象(始终)不是您认为它们的对象类型。在幕后,它创造了一个继承自你的班级的全新类。因为OOP允许子类存储在作为超类输入的变量中,所以你永远不会注意到。这是你提到的“代理”。这就是virtual函数允许延迟加载的原因。子类重写您的虚方法,并包含在返回之前延迟加载额外数据的代码。

然后,重写的属性调用将检查上下文以查看是否已加载导航属性。如果是,它只是返回它们。如果不是,它将进行额外的SQL调用以加载它们,并将它们存储在DbContext中以供日后使用。


在您更新的问题中,我的理解是运行这些代码行将导致执行3个单独的查询。

另一答案

公共虚拟属性是EF6中的延迟加载。您可以为DbContext禁用延迟加载,或者在IQueryable上使用.Include()方法在第一个查询中包含该属性。

http://www.entityframeworktutorial.net/EntityFramework4.3/lazy-loading-with-dbcontext.aspx

https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx

一旦你“遍历”列表(例如通过调用.Single(),. Count()或.ToArray()方法),就会执行查询,并且您有一个内存中的学生列表。有关查询执行的更多详细信息,请参阅https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/query-execution

第一个示例将导致3个查询,其中第一个返回具有给定Id的学生,第二个返回学生计数,第三个返回由GPA属性订购desc的前5个学生。

关于DDD =>您可以使用一些分层架构,使用ApplicationServices和DomainServices,其中DomainServices执行域逻辑,ApplicationServices加载/转换数据。

例如,https://aspnetboilerplate.com/模板是域驱动设计的良好起点。

另一答案

假设第二个代码块在“学校”实例的范围内执行(所以'this'是'School'的实例 - 在我的示例中,在school19下面),那么下面的场景是可能的:

A)您已经加载了这样的'School'实例(启用了延迟加载):

var school19 = dbContext.Set<School>()
 .FirstOrDefault(school => school.Id == 19)

然后,用于访问“学生”属性的3行代码将触发一次额外的数据库命中

var myStudent = this.Students.Single(x => x.Id == id);

执行,但随后的两个语句不会再发生数据库命中。

B)如果您已经加载了像这样的“学校”实例(启用了延迟加载):

var school19 = dbContext.Set<School>()
 .Include(school => school.Students)
 .FirstOrDefault(school => school.Id == 19)

然后,用于访问“学生”属性的3行代码不会触发额外的数据库命中。

C)如果禁用延迟加载,则

  • A)会导致空引用异常
  • B)表现相同

作为最后一句话,如果'this'是对DBContext类的实例的引用,该实例具有属性

public Set<School> Schools { get; set; }

然后它会触发3个不同的数据库调用。但结果是不同的,因为这将在所有学校的背景下执行,而我的上述假设仅适用于单个学校。

以上是关于EF6虚拟导航属性是否导致SQL查询?的主要内容,如果未能解决你的问题,请参考以下文章

EF6(代码优先)单个外键属性上的多个导航属性

EF6 - 使用可为空属性(外键,TPH)时的无效 SQL 查询

EF6 Code First Lazy Load导致空集合

EF6:导航属性外键到外键

在 EF6 中重新加载导航属性

Include 中使用的 Lambda 表达式无效。 EF6,导航属性