目录
延迟加载(LazyLoad)
如果public virtual Class Class { get; set; }(实体之间的关联属性又叫做“导航属性(Navigation Property)”)把virtual 去掉,那么下面的代码就会报空引用异常
var s = ctx.Students.First();
Console.WriteLine(s.Class.Name);
联想为什么?凭什么!!! 改成virtual观察SQL的执行。执行了两个SQL,先查询T_Students,再到T_Classes中查到对应的行。 这叫“延迟加载”(LazyLoad),只有用到关联的对象的数据,才会再去执行select 查询。注意延迟加载只在关联对象属性上,普通属性没这个东西。 注意:启用延迟加载需要配置如下两个属性(默认就是true,因此不需要去配置,只要别手贱设置为false 即可)
context.Configuration.ProxyCreationEnabled = true;
context.Configuration.LazyLoadingEnabled = true;
分析延迟加载的原理:打印一下拿到的对象的GetType(),再打印一下GetType().BaseType;我们发现拿到的对象其实是Student子类的对象。(如果和我这里结果不一致的话,说明:类不是public,没有关联的virtual 属性) 因此EF其实是动态生成了实体类对象的子类,然后override了这些virtual属性,类似于这样的 实现:
public class StudentProxy:Student
{
private Class clz;
public override Class Class
{
get
{
if(this.clz==null)
{
this.clz= ....//这里是从数据库中加载Class 对象的代码
}
return this.clz;
}
}
}
再次强调:如果要使用延迟加载,类必须是public,关联属性必须是virtual。 延迟加载(LazyLoad)的优点:用到的时候才加载,没用到的时候才加载,因此避免了一次性加载所有数据,提高了加载的速度。缺点:如果不用延迟加载,就可以一次数据库查询就可以把所有数据都取出来(使用join实现),用了延迟加载就要多次执行数据库操作,提高了数据库服务器的压力。 因此:如果关联的属性几乎都要读取到,那么就不要用延迟加载;如果关联的属性只有较小的概率(比如年龄大于7 岁的学生显示班级名字,否则就不显示)则可以启用延迟加载。这个概率到底是多少是没有一个固定的值,和数据、业务、技术架构的特点都有关系,这是需要经验和直觉,也需要测试和平衡的。 注意:启用延迟加载的时候拿到的对象是动态生成类的对象,是不可序列化的,因此不能直接放到进程外Session、Redis 等中,解决方法?
不延迟加载,怎么样一次性加
用EF永远都要把导航属性设置为virtual。又想方便(必须是virtual)又想效率高!
使用Include()方法:
var s = ctx.Students.Include("Class").First();
观察生成的SQL语句,会发现只执行一个使用join的SQL就把所有用到的数据取出来了。当然拿到的对象还是Student 的子类对象,但是不会延迟加载。(不用研究“怎么让他返回Student 对象”)
Include("Class")的意思是直接加载Student 的Class 属性的数据。注意只有关联的对象属性才可以用Include,普通字段不可以直接写"Class"可能拼写错误,如果用C#6.0,可以使用nameof语法解决问这个问题:
var s = ctx.Students.Include(nameof(Student.Class)).First();
也可以using System.Data.Entity;然后var s = ctx.Students.Include(e=>e.Class).First(); 推荐这种做法。 如果有多个属性需要一次性加载,也可以写多个Include:
var s = ctx.Students.Include(e=>e.Class) .Include(e=>e.Teacher).First();
如果Class对象还有一个School属性,也想把School对象的属性也加载,就要:
var s = ctx.Students.Include("Class").Include("Class. School").First(); 或者更好的
var s = ctx.Students.Include(nameof(Student.Class)).Include(nameof(Student.Class)+"."+nameof(Class.School)).First();