EF Core的预加载(Eager Loading)是实体框架提供的一种关联数据加载策略,核心是通过Include方法在初始查询阶段就把需要关联的实体数据一起从数据库加载回来,避免后续访问关联属性时触发额外的数据库查询,有效解决N+1查询带来的性能损耗问题。
基础Include用法
假设我们有两个实体类Student和Class,一个班级包含多个学生,学生实体关联班级实体,实体定义如下:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int ClassId { get; set; }
// 关联班级实体
public Class Class { get; set; }
}
public class Class
{
public int Id { get; set; }
public string ClassName { get; set; }
// 关联学生集合
public List<Student> Students { get; set; }
}
如果我们需要查询所有学生,同时加载每个学生所属的班级信息,就可以使用Include方法:
using (var db = new MyDbContext())
{
// 查询所有学生,同时预加载关联的Class实体
var students = db.Students
.Include(s => s.Class)
.ToList();
foreach (var student in students)
{
// 此时访问Class属性不会触发额外查询,数据已经在首次查询时加载完成
Console.WriteLine($"学生:{student.Name},班级:{student.Class.ClassName}");
}
}
多级关联加载
如果关联实体还有更深层的关联,比如班级还关联了班主任实体Teacher,我们需要加载学生、班级、班主任三级数据,可以使用ThenInclude方法:
public class Class
{
public int Id { get; set; }
public string ClassName { get; set; }
public int TeacherId { get; set; }
// 关联班主任实体
public Teacher Teacher { get; set; }
public List<Student> Students { get; set; }
}
public class Teacher
{
public int Id { get; set; }
public string TeacherName { get; set; }
}
多级加载的查询代码如下:
using (var db = new MyDbContext())
{
var students = db.Students
.Include(s => s.Class)
.ThenInclude(c => c.Teacher)
.ToList();
foreach (var student in students)
{
Console.WriteLine($"学生:{student.Name},班级:{student.Class.ClassName},班主任:{student.Class.Teacher.TeacherName}");
}
}
多个同级关联加载
如果一个实体有多个不同的关联属性,我们可以多次调用Include方法加载多个关联:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int ClassId { get; set; }
public int DormitoryId { get; set; }
public Class Class { get; set; }
// 关联宿舍实体
public Dormitory Dormitory { get; set; }
}
public class Dormitory
{
public int Id { get; set; }
public string DormitoryNo { get; set; }
}
同时加载班级和宿舍的查询代码如下:
using (var db = new MyDbContext())
{
var students = db.Students
.Include(s => s.Class)
.Include(s => s.Dormitory)
.ToList();
}
预加载带条件过滤
EF Core还支持在预加载时对关联数据进行过滤,需要使用Include配合Where条件,不过这种方式在EF Core 5.0及以上版本才原生支持:
using (var db = new MyDbContext())
{
// 查询所有班级,同时加载班级下分数大于80的学生
var classes = db.Classes
.Include(c => c.Students.Where(s => s.Score > 80))
.ToList();
}
如果是低版本EF Core,可以通过投影查询的方式实现类似效果:
using (var db = new MyDbContext())
{
var classData = db.Classes
.Select(c => new
{
Class = c,
HighScoreStudents = c.Students.Where(s => s.Score > 80).ToList()
})
.ToList();
}
预加载和其他加载方式对比
EF Core一共有三种关联数据加载方式,各自的适用场景不同:
| 加载方式 | 核心特点 | 适用场景 |
|---|---|---|
| 预加载(Eager Loading) | 首次查询就加载所有关联数据,使用Include方法 | 明确知道需要哪些关联数据,且数据量不大时 |
| 显式加载(Explicit Loading) | 后续手动触发关联数据查询,使用Load方法 | 不确定是否需要关联数据,或者关联数据量很大时 |
| 延迟加载(Lazy Loading) | 访问关联属性时才自动触发查询 | 关联数据使用频率低,且能接受额外查询开销时 |
使用注意事项
- 预加载会生成关联查询的SQL语句,如果关联层级过多或者关联数据量过大,可能会导致查询性能下降,需要合理控制预加载的范围。
- Include方法指定的关联属性必须是当前查询实体的导航属性,不能指定任意的表达式。
- 如果查询使用了
Select投影,Include方法会失效,因为投影已经指定了需要返回的字段,此时不需要再预加载关联实体。 - 预加载的数据会缓存在DbContext的跟踪器中,如果后续修改了关联数据,需要注意跟踪状态的变化。
实际开发中建议根据业务场景选择合适的加载方式,优先评估预加载带来的SQL复杂度和数据量,避免不必要的性能损耗。
EF CoreIncludeEager_Loading实体框架修改时间:2026-06-22 18:42:52