假设我有一个具有虚拟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行为?
答案 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)如果禁用延迟加载,则
作为最后一句话,如果&#39;这个&#39;是对DBContext类的实例的引用,该实例具有属性
public Set<School> Schools { get; set; }
然后它将触发3个不同的数据库调用。但结果是不同的,因为这将在所有学校的背景下执行,而我的上述假设仅适用于单个学校。