EF默认不会加载entity所关联的entity(navigation properties),比如学生(Student)床铺(SleepIn)的位置(Location):
Student zl = context.Students.Where(s => s.Name == "zl").Single(); Console.WriteLine(zl.SleepInId); //OK Console.WriteLine(zl.SleepIn.Location); //NullReference Exception
否则由于entity之间大量的、链式的、复杂的引用关系,会导致一次性的加载大量entity,耗尽系统内存资源。
但如果所有的关联entity要靠开发人员自己根据其Id查询获取,又显得累赘,所以EF提供了三种加载关联数据的模式:
关联的数据会随着entity加载同时加载。这需要用到方法:
Teacher fg = context.Teachers.Where(t => t.Name == "fg") //找到Teacher .Include(t => t.Students) //包含Teacher的Students集合, .ThenInclude(s => s.StudyIn) //再包含Student的StudyIn(Classroom) .SingleOrDefault();
如果Student不是集合,可以一直点...比如:
Teacher fg = context.Teachers.Where(t => t.Name == "fg") .Include(t => t.Major.Bases) //Major和Bases都是entity
另外,
所以,如果需要同时ThenClude()多个Pproperty,不能
Teacher fg = context.Teachers.Where(t => t.Name == "fg") .Include(t => t.Students) .ThenInclude(s => s.StudyIn) .ThenInclude(s => s.SleepIn)只能:
Teacher fg = context.Teachers.Where(t => t.Name == "fg") .Include(t => t.Students) .ThenInclude(s => s.StudyIn) .Include(t => t.Students) //看似重复,但必须这样写 .ThenInclude(s => s.SleepIn)
演示:
Bed bed = context.Find<Bed>(1); //实现已经将Id=1的Bed加载到DbContext中 var query = context.Students.Where(s => s.Name == "zl") //.Include(s => s.SleepIn) 这里没有Include学生的床铺
var query = context.Students.Where(s => s.Name == "zl") .Include(s => s.SleepIn) .Select(s => new { s.Name }); //只取Student.Name,没用到Student.SleepIn
Include中可以强制转换(多态):
.ThenInclude(t => ((Teacher)t).TeachOn) .ThenInclude(t => (t as Teacher).TeachOn)
在已经取到一个entity之后,再利用方法:
context.Entry(zl).Property("EnrollDate").CurrentValue = DateTime.Now;
context.Entry(zl) .Reference(s => s.StudyIn) .Load(); context.Entry(zl) .Collection(s => s.Teachers) .Load();演示:
在.NET core 2.1之后支持,需要添加引用Microsoft.EntityFrameworkCore.Proxies,并显式开启:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder .UseLazyLoadingProxies() .UseSqlServer(myConnectionString);所涉及的entity必须是可被继承的,且关联entity属性必须是可override的(virtual):
public class Student : Person { public virtual Bed SleepIn { get; set; } public virtual IList<Teacher> Teachers { get; set; } public virtual Classroom StudyIn { get; set; } }因为,Lazyload本质上是:
演示:
studentRep.Entry(ht) .Collection(m => m.Majors) .Query().Where(m => m.MajorId > 3) .Load();
按我们之前的方法,entity引用的集合(collection),都会一次性的把所有集合都取出来。
如果集合的数据量比较大,且我们不需要集合的全部信息,这就是一种性能浪费。比如,我们只想取:
EF为我们提供了
取出所有教室的所有学生:
foreach (var room in context.Classrooms .ToList() /* 必不可少,否则会有ADO.NET的DataReader错误 */) { Console.WriteLine(room.Name); foreach (var student in room.Students) { Console.WriteLine($" {student.Name}"); } }因为LazyLoad,外个循环里,EF会取出所有的Classrooms,但不会取出Classroom中的Student对象;
内循环里,
补充Include()
foreach (var room in context.Classrooms.Include(c=>c.Students))
优劣总结:
飞哥个人偏好:Lazy为主,Eager为附
为什么需要?
而不使用用query查询(Join):保证entity不依赖于repository(database)
没有ThenInclude,需要Select
.Include(p => p.KeywordToArticle.Select(ka=>ka.Keyword))
多快好省!前端后端,线上线下,名师精讲
更多了解 加: