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

时间:2017-12-19 10:11:52

标签: c# linq entity-framework-6 navigation-properties

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

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行为?

3 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:0)

公共虚拟属性是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 =&gt;您可以使用ApplicationServices和DomainServices的一些分层架构,其中DomainServices执行域逻辑,ApplicationServices加载/转换数据。

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

答案 2 :(得分:0)

假设第二个代码块是在“学校”实例范围内的某个地方执行的。 (所以&#39;这个&#39;是学校&#39;的一个实例 - 在我的学校19中的例子中)然后可能出现以下情况:

A)您已经加载了&#39; School&#39;像这样(启用延迟加载):

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

然后您的3行代码访问该物业&#39;学生&#39;

时会触发一个额外的数据库命中
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行代码访问该物业&#39;学生&#39;不会触发额外的数据库命中。

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

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

作为最后一句话,如果&#39;这个&#39;是对DBContext类的实例的引用,该实例具有属性

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

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