王朝网络
分享
 
 
 

MVC系列学习(三)-EF的延迟加载

王朝学院·作者佚名  2016-05-20  
宽屏版  字体: |||超大  

MVC系列学习(三)-EF的延迟加载1.什么叫延迟加载

字面上可以理解为,一个动作本该立即执行的动作,没有立即执行

2.从代码上理解

static void Main(string[] args){ //执行该语句的时候,查看sql监视器,发现并没有生成sql语句 IEnumerable<Student> stu = dbContext.Students.Where(s => s.Id == 1).Select(s => s); //只有当 使用的时候 ,才生成sql语句 Student student = stu.FirstOrDefault();}

只有对象被使用了,才生成sql语句

3.寻找原因,什么原因导致延迟加载

先理解两个Where()方法:

a.集合的Where()

List<string> listStr = new List<string>(){ "A", "BB", "CCC"};string bb = listStr.Where(s => s.Length == 2).Select(s => s).FirstOrDefault();

转到Where的定义,发现 集合 中的Where方法 实际上是IEnumerable的扩展方法,该接口继承与IEnumerable

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> PRedicate);

public interface IEnumerable<out T> : IEnumerable

b.DbSet中的Where方法

dbContext.Students是DbSet<Student>类型,所以此处说 DbSet的中Where方法

var stu = dbContext.Students.Where(s => s.Id == 1);

public partial class SchoolEntities : DbContext{ public virtual DbSet<Student> Students { get; set; }}

dbContext.Students是DbSet<T>类型的,转到DbSet<T>定义看看

此处的Where()是IQueryable的扩展方法,继承与IQueryable

public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

public interface IQueryable<out T> : IEnumerable<T>, IQueryable, IEnumerable

public interface IQueryable : IEnumerable

 

c.IEnumerable与IQueryable的区别

var s1 = dbContext.Students.Where(s => s.Id == 1); var s2 = s1.Where(s => s.Age > 0); var s3 = s2.Select(s => s).FirstOrDefault();

用sql监视器查看,发现总共就执行了一次sql查询

得出结论:

1.实现EF延迟加载 的 实际上是IQueryable上的扩展方法,更具体的话是DbQuery类型来实现的 2.IEnumerable<T>将命令直接执行,第一次执行后的结果,保存到内存,不会拼接命令树 3.IQueryable<T>将语句拼接成一个命令树,当用到的时候,再执行 4.两种操作都只访问了一次数据库

4.为什么要有延迟加载

a.无法确定 本次查询条件 是否 已经添加结束

DbQuery<Student> s1 = dbContext.Students.Where(s => s.Id == 1).Where(s => s.Age > 0) as DbQuery<Student>;

每次添加 查询条件的时候,都只是返回 包含所有添加条件的 DbQuery对象,只有最后使用的时候,才根据条件生成相应的sql语句

b.对于外键实体,按需加载

本次需要用到的两张 具有 主外键关系的两张表如下

var tea = dbContext.Teachers.Where(t => t.tId == 1);//生成sql语句,如图 代码一Teacher teacher = tea.FirstOrDefault();//生成sql语句,如图 代码二string className = teacher.TeachClass.cName;

代码一,如下图:

SELECT TOP (1) [Extent1].[tId] AS [tId], [Extent1].[tName] AS [tName], [Extent1].[tAge] AS [tAge], [Extent1].[tClass] AS [tClass] FROM [dbo].[Teacher] AS [Extent1] WHERE 1 = [Extent1].[tId]

代码二,如下图:

exec sp_executesql N'SELECT [Extent1].[cId] AS [cId], [Extent1].[cName] AS [cName] FROM [dbo].[TeachClass] AS [Extent1] WHERE [Extent1].[cId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

观察,得出结论:

1.按需加载:在第一次执行的时候,因为没有用到外键属性,所以生成sql语句的时候,不会去去查询 TeachClass表 2.当EF需要用到外键属性的时候,才会去加载 相应的表

c.按需加载的缺点:

实例代码如下:

DbQuery<Teacher> teachers = dbContext.Teachers;StringBuilder sbTeacher=new StringBuilder(100);foreach (Teacher tea in teachers){ //每次调用 外键表Teachers上 的 外键实体时,都会去查询数据库 //EF有个优化,相同的外键实体只查一次,即TeachClass相同只查一次 sbTeacher.Append(tea.TeachClass.cName);}

生成的SQL脚本如下:

exec sp_executesql N'SELECT [Extent1].[cId] AS [cId], [Extent1].[cName] AS [cName] FROM [dbo].[TeachClass] AS [Extent1] WHERE [Extent1].[cId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

第二次,和第三次,因为TeachClass的值相同,则只查询了一次

exec sp_executesql N'SELECT [Extent1].[cId] AS [cId], [Extent1].[cName] AS [cName] FROM [dbo].[TeachClass] AS [Extent1] WHERE [Extent1].[cId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=2

5.连接查询

既然EF是只有用到外键实体的时候,才加载相应表,那么如果我们要连接两张表要怎么做

a.通过Include方法

DbQuery<Teacher> teachers = dbContext.Teachers.Include("TeachClass");StringBuilder sbTeacher=new StringBuilder(100);foreach (Teacher tea in teachers){ //只有第一次 查询的使用,将数据查询 并保存到内存中, //接下来的操作只是在内存中读取,并没有读取数据库 sbTeacher.Append(tea.TeachClass.cName);}

查看sql语句,发现 EF为我们生成 left outer  join ,连接了两张表

SELECT [Extent1].[tId] AS [tId], [Extent1].[tName] AS [tName], [Extent1].[tAge] AS [tAge], [Extent1].[tClass] AS [tClass], [Extent2].[cId] AS [cId], [Extent2].[cName] AS [cName] FROM [dbo].[Teacher] AS [Extent1] LEFT OUTER JOIN [dbo].[TeachClass] AS [Extent2] ON [Extent1].[tClass] = [Extent2].[cId]

b.生成 join 的另一种方式

var teachers = dbContext.Teachers.Select(t => new {tName = t.tName, ClassName = t.TeachClass.cName}).ToList();

生成的sql语句如下

SELECT 1 AS [C1], [Extent1].[tName] AS [tName], [Extent2].[cName] AS [cName] FROM [dbo].[Teacher] AS [Extent1] LEFT OUTER JOIN [dbo].[TeachClass] AS [Extent2] ON [Extent1].[tClass] = [Extent2].[cId]

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有