首页 > 技术文章 > EF的延迟加载

bemad 原文

什么是延迟加载?
通俗的是说,就是按需要加载,也就是需要数据的时候再加载EF默认会进行延迟加载

为什么要使用延迟加载?
你的身边是不是有朋友在使用EF开发的时候总是喜欢在每一句的后面加上一个ToList(),
其实这样是不对的!
在每次执行ToList()或者FirstOrDefault()等函数操作时,EF都会去请求一次数据库。
这里说的是数据查询操作,至于CUD这些操作会在SaveChange()时进行数据库的请求
比如这样一段代码:(仅举例)

var users = EntityTest.Users.Where(u=>u.Adress=="湖北");//过滤地址为湖北的
var userMale = users.Where(u=>u.gender==1).ToList();//过滤出性别为男的
var userFemale = users.Where(u=>u.gender==0).ToList();//过滤出性别为女的

在过滤湖北的这一行,其实并没有进行数据库的请求,只是Where方法将所写的lambda表达式保存为了Sql语句,在进行性别过滤进行ToList()之后才访问了数据库。
好处:用时才加载,保证了数据的实时性,另外在做多条件查询时只要是先拼凑查询语句,最后再执行数据库访问,降低了服务器的负担。

延迟加载的实现:
上面说到,Where方法将所写的lambda表达式保存为了Sql语句,通过智能提示,发现此时users返回的是一个IQueryable类型的数据,并且Where的第一个参数是this前缀,
由此得出Where并不是在IQueryable接口内部的,而是在外部类通过扩展方法的方式加上去的,这个类就是Queryable

但是为何Where方法是从Entity这个EF上下文对象中点出来的?
通过F2进入EF上下文中的DbSet 查看 , 原来是继承了IQueryable接口

因此,EF的延迟加载是依靠Queryable实现的

Queryable从何而来?
在System.Linq命名空间下,有两个静态类:
①:Enumerable类,它针对继承了IEnumerable接口的集合进行扩展;
②:Queryable类,它针对继承了IQueryable接口的集合类进行扩展。接口IQueryable也是继承了IEnumerable接口的,所以,致使两个接口的方法在很大程度上时一致的。
IEnumerable接口是本地内存中的数据读取,而IQueryable接口是远程数据的读取。
如上所说IQueryable中的Where是将表达式保存为Sql,IEnumerable接口中的Where就是用来过滤本地数据的。

实现步骤:
每次在执行where查询操作符的时候IQueryProvider会为我们创建一个新的IQueryable,调用AsEnumerable()方法的时候并不会去实际取值,只是
得到了一个IEnumerable,所以EF在查询数据时候不要先取IEnumerable再去筛选数据。执行ToList方法时才会去真正调用迭代器GetEnumerator()
取值。真正取值时候,会去执行IQueryProvider中的Excute方法.(解析表达式,然后执行取得结果))
这就是IQueryable的延迟加载

推荐阅读