禁用延迟加载仍然加载相关实体。为什么?

imm*_*rza 3 c# entity-framework

我已禁用延迟加载,但当访问引用实体/实体的属性时,实体会自动从数据库加载。为什么?应该不能再访问了吧?

My EF version="6.1.3"
Run Code Online (Sandbox Code Playgroud)

以下行加载部门数据,而我期望它不应该加载,因为我已禁用延迟加载并且导航属性是非虚拟的。

Student student  = context.Student.select(x => x.Department).First();
Run Code Online (Sandbox Code Playgroud)

课程:

public class Student
{
    public string Name { get; set;}
    public string Age { get; set;}
    public string Date { get; set;}

    public Department department { get; set }
}

public class Department
{
    public string Id { get; set; }
    public class Name { get; set; }
    public Student Student { get; set }
}
Run Code Online (Sandbox Code Playgroud)

语境

public StudentContext : DbContext
{
    public StudentContext()
    {
        this.Configuration.LazyLoadingEnabled = false;

    }

}
Run Code Online (Sandbox Code Playgroud)

Ser*_*kiy 5

什么是延迟加载?

执行一些初始 SQL 查询并加载一些实体后,它可以通过执行单独的数据库查询来加载相关数据。

首先让我们回顾一下你的课程:

  • 部门与学生不是1:1的关系。每个系可以容纳很多学生
  • 学生没有身份证件
  • 部门应该有整数ID
  • 为了支持延迟加载导航属性应该是虚拟的

考虑到这些,您将获得以下课程:

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Age { get; set; }
    public string Date { get; set; }

    public virtual Department Department { get; set; }
}

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Student> Students { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在让我们看看启用延迟加载时会发生什么。例如,您正在加载学生:

Student bob  = context.Students.FirstOrDefault(s => s.Name == "Bob");
Run Code Online (Sandbox Code Playgroud)

这将生成类似于

SELECT TOP(1) Name, Age, Date, Department_Id 
FROM Students
WHERE Name = 'Bob' --actually name will be passed as query parameter
Run Code Online (Sandbox Code Playgroud)

更重要的是,该 SQL 查询将被执行,来自服务器的响应将被加载到内存中并映射到Student实体。除了已加载其 ID 外,没有与部门相关的任何内容。现在,如果您尝试获取部门详细信息

 var departmentName = bob.Department?.Name;
Run Code Online (Sandbox Code Playgroud)

EF 将生成 SQL 查询以加载相关部门的详细信息。就像是

 SELECT d.Id, d.Name 
 FROM Students s
 INNER JOIN Departments d ON s.Department_Id = d.Id
 WHERE s.Id = 42 -- assume Bob has this id
Run Code Online (Sandbox Code Playgroud)

这个查询将被执行。这是服务器的第二次往返。这就是延迟加载的工作原理。

延迟加载被禁用时,将不会对服务器进行第二次查询。您将只有第一次查询时加载的数据。即部门实体将是null,您将无法获得它的名称。


现在您的情况 - 您正在通过第一次查询加载部门数据。实际上你的代码不会编译,因为你应该使用Department实体:

Department dep = context.Student.Select(x => x.Department).First();
Run Code Online (Sandbox Code Playgroud)

启用或禁用延迟加载不会影响它。因为正如我刚刚写的,延迟加载会影响加载相关数据的进一步查询。但首先不是加载初始数据的查询。在你的情况下,它看起来像

SELECT TOP(1) d.Id, d.Name
FROM Students s
LEFT OUTER JOIN Departments d ON s.Department_Id = d.Id
Run Code Online (Sandbox Code Playgroud)