Entity Framework中virtual关键字的作用及相关疑问
EF中virtual导航属性的作用及你的疑问解答
先贴出你提供的实体类代码:
public class Student { public int StudentId; public string StudentName; public int CourseId; public virtual Course Courses { get; set; } } public class Course { public int CourseId; public string CourseName; public string Description; public ICollection<Student> Students {get;set;} public ICollection<Lecture> Lectures { get; set; } } public class Lecture { public int LectureId; public string LectureName; public int CourseId; public virtual Course Courses { get; set; } }
一、virtual关键字的核心作用:支持EF的代理懒加载
在EF6以及默认配置的EF Core里,virtual是实现代理懒加载必不可少的前提:
- EF会给你的实体类动态生成一个子类(代理类),这个子类会重写标记为
virtual的导航属性。当你第一次访问这个导航属性时,代理类会自动触发数据库查询,把关联的数据加载进来——这就是懒加载的核心运作方式。 - 要是导航属性不加
virtual,EF没法生成代理类拦截属性访问,自然触发不了自动加载的逻辑。
二、解答你的两个疑问
1. 为什么_context.Lecture.FirstOrDefault()返回的Lecture不含Course属性?
懒加载不是在查询实体的时候就加载关联数据,而是第一次访问导航属性的时候才触发:
- 执行
_context.Lecture.FirstOrDefault()时,EF只会查Lecture表的数据,返回的对象里Courses是未加载状态(通常是null或者空的代理集合)。 - 只有当你后续代码里访问
lecture.Courses(比如var course = lecture.Courses;),而且DbContext还没被释放的情况下,EF才会自动跑一次数据库查询,把对应的Course数据拉进来。 - 你用
Include("Courses")的方式属于显式加载(Eager Loading),是查询Lecture的时候就一次性把关联的Course数据一起查出来,和懒加载是完全不同的加载策略。
2. 为什么感觉不用virtual也能实现懒加载?
这要么是混淆了加载方式,要么是用了EF Core的特殊配置:
- 如果你用的是
Include,这是显式加载,和virtual没关系,不管加不加都能这么用。 - EF Core 2.1之后支持无代理懒加载,开启这个功能后不用加
virtual也能懒加载,但这不是默认配置,需要手动安装Microsoft.EntityFrameworkCore.Proxies包,再在DbContext的配置里开启。 - 要是没开无代理懒加载却觉得不用
virtual也能懒加载,大概率是调试器的锅:调试时查看对象属性,调试器会自动访问属性触发懒加载(此时上下文还活着),让你误以为是自动加载的,但实际上如果代码里没主动访问属性,数据根本不会被加载。
三、总结什么时候需要virtual
- 用EF6或者EF Core默认的代理懒加载?必须给导航属性加
virtual,不然懒加载直接失效。 - 用EF Core的无代理懒加载?可以不用
virtual,但这是特殊场景的配置,不是默认行为。 Include是显式加载,和virtual无关,它的作用是提前加载关联数据,避免后续多次查询数据库的N+1问题。
内容的提问来源于stack exchange,提问作者ltog
相关产品推荐
相关产品推荐

