第五节: EF高级属性(一) 之 本地缓存、立即加载、延迟加载(不含导航属性)
一. 本地缓存
从这个章节开始,介绍一下EF的一些高级特性,这里介绍的首先介绍的EF的本地缓存,在前面的“EF增删改”章节中介绍过该特性(SaveChanges一次性会作用于本地缓存中所有的状态的变化),在这里介绍一下本地缓存的另外一个用途。
① Find方法通过主键查询数据,主键相同的查询,只有第一次访问数据库,其它均从缓存中读取。
② 延迟加载的数据,在第一次使用的使用时访问数据库,后面无论再使用多少次,均是从内存中读取了。
1 Console.WriteLine("--------------------------- 1.本地缓存属性 ------------------------------------"); 2 db.Database.Log += c => Console.WriteLine(c); 3 //以下4个根据主键id查询,查询了一次,都存到本地缓存里了,所以user2不查询数据库,但user3的id不同,所以查询数据库 4 var user1 = db.Set<TestInfor>().Find("2"); 5 var user2 = db.Set<TestInfor>().Find("2"); 6 var user3 = db.Set<TestInfor>().Find("3");
二. 立即加载
这里的立即加载指单表,不含主外键的情况。
所谓的立即加载就是代码执行的时候直接去数据库查询,与是否立即使用无关,查出来后放到本地缓存里,以后再次使用的时候,从本地缓存中读取。
常见的立即加载的的标记:toList() 、FirstOrDefault() 。
1 Console.WriteLine("--------------------------- 2.即时加载 ------------------------------------"); 2 3 ////以下3个属于立即查询,所以每次都要查询数据库,不缓存 4 var user5 = db.Set<TestInfor>().Where(u => u.id == "2").FirstOrDefault(); 5 var user6 = db.Set<TestInfor>().Where(u => u.id == "2").FirstOrDefault(); 6 var user7 = db.Set<TestInfor>().Where(u => u.id == "2").FirstOrDefault();
三. 延迟加载
这里的延迟加载指单表,不含主外键的情况。
1. 定义:只有我们需要数据的时候,才去数据库查询
比如:我们需要根据条件判断,通过Where来拼接条件(IQueryable),在拼接的过程中,并没有访问数据库,只有在最终使用的时候,才访问数据库。
特别注意:调用的时候要foreach循环来调用,只有第一次使用的时候去访问数据库,其它的都是从本地缓存中读取。
2. 禁用延迟加载的方法:
a:如果结果是集合,在拼接的结尾加 toList() ,其它类型调用其它方法
b:如果结果是单个实体,在拼接的结尾加 FirstOrDefault()
3. 好处:保证了数据的实时性,什么时候用,什么时候查询
4. 弊端:每用一次,就需要查询一次数据,服务器压力大
5. 延迟加载的实际开发场景:
分页要要经历where多个条件查询、skip、take、toList,如果每调用一个方法都连接一个数据库,那么在拼接过程中就访问了3次数据库,而且可能数据量非常多,所以这个时候使用延迟加载,只有在所有sql语句拼接完的最后一步才连接数据库。
总结:只要查询结果实现了IQueryable接口类的,那么查询结果都是延迟加载的。
1 Console.WriteLine("--------------------------- 3.延迟加载 ------------------------------------"); 2 IQueryable<TestInfor> user4 = db.Set<TestInfor>().Where(u => u.id != "123"); 3 IQueryable<TestInfor> user6 = db.Set<TestInfor>().Where(u => u.id != "123"); 4 foreach (var item in user4) 5 { 6 Console.WriteLine("我要从数据库中读取数据了:" + item.txt1); 7 } 8 foreach (var item in user4) 9 { 10 Console.WriteLine("我要从数据库中读取数据了2:" + item.txt1); 11 } 12 foreach (var item in user6) 13 { 14 Console.WriteLine("我要从数据库中读取数据了3:" + item.txt1); 15 }
延迟加载上述案例分析:
* IQueryable类型的 user4和user6, 都是延迟加载的,下面foreach第一次使用该对象的时候去数据库查询。
* 这里会有这么几个问题:
* ①:foreach第一次遍历的时候去数据库中查询user4,然后放到本地缓存里,后面无论循环多少次,都是从本地缓存中读取user4。
* ②:前两个foreach操控的对象都是user4,所以第二个foreach无论哪次循环,都是从本地缓存中读取
* ③:第三个foreach操控的对象是user6,同样是在foreach第一次循环的时候去数据库查询,所以在代码执行到第一个或第二个foreach的时候,
* 手动去数据库改数据,当执行到第三个foreach,查询出来的数据就是修改后的了。