EF(Entity Framework)是微软的一个ORM框架

使用过EF的同学都知道它有一个延迟加载的功能

那么这个延迟加载的功能到底是什么?

为什么需要延迟加载?

使用延迟加载的优点和缺点又各是什么?

可以通过一个简单的小例子来阐述EF的这些问题

首先使用到了两个很简单的数据表

关系图如下:

T_Product的Uid关联到T_Users的Id,形成一个外键关系

是不是真的很简单= =

然后在测试项目中根据数据库添加EF数据模型

准备工作已经做好了,现在进入主题

首先需要搞明白的是:什么是延迟加载?

延迟加载又可以理解成 按需加载

根据字面的意思很容易理解:按照所需的数据 加载数据

上例子:

很简单的代码,相信大家都看得懂

接下来设置断点对代码的执行步骤进行分解

首先我在如图所示的位置设置了一个断点

打开sql server profiler监听接收到的sql语句

运行程序并命中断点

此时程序停留在到数据库取数据(姑且这么认为)的这行代码上

由于代码还未执行所以数据库不可能接收到任何的sql语句

按下F11逐语句执行

可以看到,程序跳转,数据库接收到sql语句

正是var user = dbEntities.T_Users.Where(u => u.Id == 2).FirstOrDefault();这行代码发送了sql语句到数据库取的数据

那么所谓的延迟加载体现在哪里呢?

其实这行代码是变相的即使加载,看起来像是调用where方法之后马上到数据库取出了数据,但是别忽略了后面的FirstOrDefault()

在EF中,调用where方法只是先保存了sql语句,但是并没有提交到数据库

但是当调用了FirstOrDefault()方法的时候就表示程序要用数据了,这时sql语句才会被提交到数据库中

这就是延迟加载,到要使用数据的时候才取出数据

口说无凭,下面继续用代码来证明这个说法

将代码拆分如下,还是在调用where方法的地方设置断点并运行

F11下一步

这时可以看到,代码已经执行了where方法,并停在了.FirstOrDefault方法,但是此时数据库并没有接收到任何sql语句

为了方便看清楚,在Console.ReadKey()也设置了一个断点,F5代码停留在Console.ReadKey()

现在已经很明显了,只有程序中要使用数据的时候(调用了FirstOrDefault),sql语句才会被发送发数据库

这就是EF的延迟加载

但是List集合本身就有一个where方法

难道这个where方法要是延迟加载的吗,但是这并不是到数据库取数据的呀

在上图的智能提示中可以隐约的看到,该where方法的第一个参数是一个IEnumerable的linq表达式

我们可以在var user = dbEntities.T_Users.Where(u => u.Id == 2)这行代码上对where方法按F12转到定义看一看

注意看一下这个where方法的返回值类型,是IQueryable而不是IEnumerable

这说明了两个where方法虽然名字一样,甚至连参数都差不多但是一个返回的是IEnumerable另外一个则是IQueryable

这就从本质上区别开了这两个where方法

同时注意一下IQueryable的where方法第一个参数的this关键字,这说明了什么?

说明了where并不是在IQueryable接口内部的,而是在外部类通过扩展方法的方式加上去的(这个类就是Queryable,该类中为IQueryable接口提供了n多类似where的扩展方法)

所以可以说,EF能实现延迟加载就是因为有Queryable类的存在

但是从这行代码中我们又可以看到

var user = dbEntities.T_Users.Where(u => u.Id == 2);

where方法是从dbEntities这个EF上下文对象的T_Users属性中点出来的

where方法不是在Queryable类中的吗(或者说是IQueryable接口的)

怎么就可以从EF的上下文属性中调用呢?

那么现在我们就找到EF上下文,看看T_Users属性到底是怎么回事

可以很清楚的看到T_Users是一个DbSet<T_Users>类型的泛型属性

F12转到定义看一下这个DbSet是什么东西

是不是能够在DbSet的基类(接口中)找到IQueryable!

这就解释了为什么能够在EF上下文的T_Users属性中点出where方法并使用

因为T_Users的类型继承了IQueryable接口,自然能够使用IQueryable接口中的方法啦!

上面的长篇大论简要的解释了一下EF的延迟加载机制

说了那么多,那到底为什么需要延迟加载呢?

直接取直接用不就好了?

确实从这个简单的例子来看延迟加载貌似很多余

但是通常我们操作数据库不可能只是这么简单的

就拿很常用的分页来说

一般是先对数据进行排序

然后按照要求跳过几行数据

在取几行数据

这就不是一个简单的where方法可以实现的了

至少需要先调用order进行排序,然后skip跳过几行数据,最后take取几行数据

如果where/order/skip/take等等方法每次使用的时候就马上提交sql语句到数据库

那做一个分页查询至少要发送4次请求

也就是说要和数据库交互4次

如果使用延迟加载的话

上面的where/order/skip/take方法调用的时候可以看做只是在拼凑条件

当条件满足的时候(一般就是要用数据的时候,比如说FirstOrDefault方法)

在将整个拼凑好的sql语句一起提交到数据库

这样一来和数据库的交互次数由4降到了1

这就是使用延迟加载的本质原因之一:拼凑条件一起提交,降低数据库交互次数,提高数据库的吞吐量

这也是延迟加载的优点

在本例中,T_Products中有一个外键Uid关联到T_Users

上代码:

var product = dbEntities.T_Products.Where(p => p.Uid == 2).FirstOrDefault();

Console.WriteLine(product.T_Users.UserName);

表内容如下

可以看到Uid为2有两行

取出Uid为2的T_Products集合然后获得第一行的实体

并将其关联的外键属性T_User的UserName输出

继续在每行代码设置断点并运行

程序停留在上图的位置,F5执行,命中下一个断点

可以看到,因为调用了FirstOrDefault方法

所以想数据库发送了sql语句

但是注意观察左边的sql语句

并没有将外键列一起查询出来

继续F5运行

还是观察左边的sql语句

因为执行了Console.WriteLine(product.T_Users.UserName);这行代码

需要用到外键列

此时EF才向数据库提交查询外键列的sql语句

这就是延迟加载的本质原因之二:针对外键属性,EF只有在这个外键属性用到的时候才会去查询

那么通过上面的介绍,EF延迟加载的优点已经很明显了

那么缺点是什么呢?

比如说现在的需求是

取出T_Products

var products = dbEntities.T_Products.Where(p => true);
foreach (var p in products)
{
Console.WriteLine(p.T_Users.UserName);
} Console.ReadKey();

表中的所有实体

遍历输出每一个实体的外键T_Users并输出其UserName

我们知道

在执行完第一行代码的时候并没有和数据库进行交互

但是此时问题来了

foreach每次一次循环都要用一次products集合中实体的外键

这就造成了每循环一次就到数据库中取一次数据

这就是EF延迟加载的缺点:每次用到外键列都会去查询一次数据库

其实这里EF内部已经做了一些列的小优化,对于相同的实体只取一次,比如说T_Products表中Uid为2的有两行,Uid为3的有两行,本来需要查询4次,经过优化之后只需要查询两次

但是当数据量十分巨大的时候,这些小优化起到的作用微乎其微

那么要怎么避免这个缺陷呢?

这时可以通过连接查询先将外键列一起查询出来在循环使用

代码如下:

var products = dbEntities.T_Products.Include("T_Users");//通过Include告诉EF连接查询哪个外键列,特别舒服的事情是,Include可以一直点下去~!
foreach (var p in products)
{
Console.WriteLine(p.T_Users.UserName);
} Console.ReadKey();

点击运行可以看到下图的效果

注意左边的sql语句

生成了inner join的sql语句

只进行了一次数据库交互

EF使用延迟加载的本质原因的更多相关文章

  1. Entity FrameWork 延迟加载的本质(一)

    1.集合的标准查询运算符方法,是来自于System.Linq.Enumerable里给IEnumerable接口添加的扩展方法 2.EF上下文里的DBSet<T>里的标准查询运算符方法,来 ...

  2. EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)

    前言 终于踏出第一步探索EF Core原理和本质,过程虽然比较漫长且枯燥乏味还得反复论证,其中滋味自知,EF Core的强大想必不用我再过多废话,有时候我们是否思考过背后到底做了些什么,到底怎么实现的 ...

  3. Cookies 初识 Dotnetspider EF 6.x、EF Core实现dynamic动态查询和EF Core注入多个上下文实例池你知道有什么问题? EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)

    Cookies   1.创建HttpCookies Cookie=new HttpCookies("CookieName");2.添加内容Cookie.Values.Add(&qu ...

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

    1.什么叫延迟加载 字面上可以理解为,一个动作本该立即执行的动作,没有立即执行 2.从代码上理解 static void Main(string[] args) { //执行该语句的时候,查看sql监 ...

  5. Python 编码错误的本质原因

    转载自:https://foofish.net/python-unicode-error.html 不论你是有着多年经验的 Python 老司机还是刚入门 Python 不久的新贵,你一定遇到过Uni ...

  6. Android内存泄漏的本质原因、解决办法、操作实例

    今年最后一个迭代终于结束了,把过程中碰到的不熟悉的东西拉出来学习总结一下   内存泄漏的本质是:[一个(巨大的)短生命周期对象的引用被一个长生命周期(异步生命周期)的对象持有]   这个东西分为两个部 ...

  7. MVC ---- EF的延迟加载

    //EF中的where 有延迟加载功能(Iqueryable中的where) Sys_Log pEdit = nb.Sys_Log.Where(p=>p.F_Account== "su ...

  8. EF的延迟加载LazyLoad

    延迟加载只对 关联/导航 属性(Navigation Property)有用,普通属性没有这个东西. 延迟加载是一条一条的读取属性,调用一次,读取一次. 条件: context.Configurati ...

  9. EF中延迟加载的那些事

    延迟加载又称懒加载,通俗一点就是关联了一个对象,不用的时候不去查这个对象,当调用的时候再组织sql去查出这个对象的相关内容. 一.在使用EF时,我们会发现借助于框架生成的实体类中的的导航属性通常是标记 ...

随机推荐

  1. android Toolbox和BusyBox

    在安卓系统中,Toolbox是能够实现内存的管理,备份管理和清除数据等功能的系统文件.用来对手机性能进行设置,需要root权限.能够被软件调用. 我们在updater-script文件中,知道有类似s ...

  2. AB序列 凹函数的性质

    链接:https://www.nowcoder.com/acm/contest/113/B来源:牛客网 题目描述 给长度为n的序列A,长度为m的序列B.可以给A序列里每个元素加上x且B序列里每个元素减 ...

  3. 汕头市队赛 SRM 09 C 撕书

    C 撕书III-3 SRM 09 背景&&描述 琉璃双在撕书.     书总共有n页,每页都可以看作是一个数字.     琉璃读书喜欢来回地读.但他也因此发现了作者的灌水行为:有些连续 ...

  4. 洛谷 P2916 [USACO08NOV]为母牛欢呼Cheering up the C…

    题目描述 Farmer John has grown so lazy that he no longer wants to continue maintaining the cow paths tha ...

  5. 调用SMTP发送有附件的邮件

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  6. bananahill(NOIP模拟赛Round 8)

    题目描述 香蕉川由座香蕉山组成,第i座山有它的高度.小Z准备从左到右爬这里的恰好座香蕉山,但他不希望山的高度起伏太大,太过颠簸,会让本就体育不好的他过于劳累.所以他定义了爬山的劳累度是所有爬的相邻的两 ...

  7. 通过OpenGL ES在iOS平台实践增强现实(一)

    http://ios.9tech.cn/news/2013/1108/38495.html 1.本文采用OpenGL ES 1固定渲染管线实现,目标为在设备拍摄到的现实世界中,绘制世界坐标轴,并根据设 ...

  8. Linux内存管理图解【转】

    转自:http://www.360doc.com/content/13/0505/15/12218157_283128759.shtml Linux内存管理图解 2013-05-05  果儿的百科   ...

  9. 华为上机测试题(Excel表格纵列字母数字转换-java)

    PS:这是我刚做的一道题,题目不难,满分60,得分40,大家看看哪里有问题,欢迎提意见,感谢! /* * 题目:Excel表格纵列字母数字转换 * 描述: 在Excel中列的编号为A-Z,AA-AZ, ...

  10. HTML+JavaScript制作表白特效,表白不成功,小编现场吃雪

    今年的雪特别美,长沙自从08年后的最大的一场学了,今天小编给大家制作一个表白特效,希望大家喜欢,如果你是程序员希望对你有帮助,追到你喜欢的女孩,哈哈~追不到对象,小编现场吃学给你大家看 下图是爱心飘落 ...