DbContext 查询(二)
接上一篇《DbContext 查询》。
对本地数据运行LINQ查询
由上篇博客可得知,Local属性返回的是内存中的数据集合,那使用LINQ to Object我们可以对这些数据运行查询。
查看一下示例:Example 2-21
1 private static void LocalLinqQueries()
3 using ( var context = new BreakAwayContext())
4 {
5 context.Destinations.Load();
6
7 var sortedDestinations = from d in context.Destinations.Local
8 orderby d.Name
9 select d;
10
11 Console.WriteLine( " All Destinations: ");
12 foreach ( var destination in sortedDestinations)
13 {
14 Console.WriteLine(destination.Name);
15 }
16
17 var aussieDestinations = from d in context.Destinations.Local
18 where d.Country == " Australia "
19 select d;
20
21 Console.WriteLine();
22 Console.WriteLine( " Australian Destinations: ");
23 foreach ( var destination in aussieDestinations)
24 {
25 Console.WriteLine(destination.Name);
26 }
27 }
28 }
上面这段代码加载所有Destinations到内存中,然后对内存中的数据运行根据名称以及根据国家的筛选查询,记住Find方法默认也是优先从内存中获取数据的。
使用Load以及Local在减轻数据库查询方面是蛮好的,但是记住把所有的数据都加载到内存中是一个昂贵的操作,如果你运行多个查询而仅仅是返回你数据的子集,你可以仅仅把你需要的数据加载到内存中,而不是把所有数据。
各LINQ提供程序之间的区别
针对DbSet以及Local的查询有一些微妙但重要的区别,这两个数据源使用两种不同的LINQ 提供程序。DbSet使用的是LINQ to Entities:针对Entity Framework使用Model和映射来将你的查询转换成SQL,Local使用的是LINQ to Object:使用标准的.NET操作来对数据进行筛选排序等操作。同样的查询语法会返回不同的结果,取决于你使用的LINQ提供程序类型。比如:数据库比对String类型是大小写不敏感的,但是.NET是大小写敏感的。这就造成同样的查询条件返回的结果是不一致的。
大部分的LINQ 提供程序支持同样的核心功能,但仍然有一些区别,比如:LINQ to Object支持LAST 操作但是 LINQ to Entities不支持这个操作。
操作ObservableCollection
如果你仔细查看Local的返回类型,你会发现返回类型是ObservableCollection <TEntity>,这种类型集合被修改后就会通知其订阅者,ObservableCollection在大部分的数据绑定(data-binding)场景下是有用的。同样在你的应用程序需要知道什么时候新数据被加载到内存中也是有用的。
只要Local的内容发生变化,会引发CollectionChanged事件,比如:数据从数据库读取到内存中、新的对象被添加到DbContext,内存中的对象被标记为删除等。
查看如下示例:Example 2-22
1 private static void ListenToLocalChanges()
3 using ( var context = new BreakAwayContext())
4 {
5 context.Destinations.Local.CollectionChanged += (sender, args) =>
6 {
7 if (args.NewItems != null)
8 {
9 foreach (Destination item in args.NewItems)
10 {
11 Console.WriteLine( " Added: " + item.Name);
12 }
13 }
14
15 if (args.OldItems != null)
16 {
17 foreach (Destination item in args.OldItems)
18 {
19 Console.WriteLine( " Removed: " + item.Name);
20 }
21 }
22 };
23
24 context.Destinations.Load();
25 }
26 }
以上代码演示的是当有数据被添加或删除的时候,会打印出被添加或删除的数据,然后再从数据库中重新加载一次Destinations的数据到内存中。
如果你的界面需要根据数据的变化及时的要刷新显示,那这个事件将是非常便利的。
一些UI框架,比如WPF,会自动帮你做好这方面的工作:如果你绑定Local数据到WPF控件ListBox,无论何时何处数据变化了,ListBox都会及时跟新显示。
注意:如果你对Local数据运行LINQ查询的话,查询返回的结果将不再是ObservableCollection类型的,那你懂的,把这个结果绑定给WPF ListBox,ListBox就不会自动跟新 显示了,将需要你手动注册OnCollectionChanged事件来处理刷新逻辑!
加载相关数据(Loading Related Data)
到现在为止我们处理的都是单一类型的实体数据,比如Destinations,但如果我们编写一个真实的应用的话,我们肯定想知道Destinations的Lodgings,如果我们想获取Destinations相关联的Lodgings,这就意味着我们要处理关联数据。
我们将需要把相关的数据载入到内存中以让我们能够操作,这里有三个方法你可用来加载相关数据:Lazy Loading、Eager Loading、Explicit Loading。这三种方法可能会返回同样的结果,但对性能的影响是有一些区别的,决定使用哪个取决于不同的使用场景,并不是一锤子买卖,下面我们详细探讨这三个方法。
Lazy Loading
就是我们常说的延迟加载:你试图访问关联数据的时候,EF会自动为你获取这部分数据。比如:如果你有一个叫Grand Canyon的Destination被加载了,之后你想使用Destination的Lodgings属性,EF将会自动的对数据库发出一个查询请求来加载特定Destination的Lodgings,表面看起来好像Lodgings属性好像很自然的就可以随意获取访问。
EF内部是通过dynamic proxy来实现Lazy Loading的:当EF返回一个查询结果时,它会创建用户定义的类的实例并用数据库返回的数据填充这些实例。EF有一种能力能够在运行时动态的创建一个新的类型,这个新的类型继承自用户定义的POCO类(关于POCO类各位自行补脑)。这个新类的行为就像是用户POCO类的动态代理(dynamic proxy)一样。它会重写用户POCO类的导航属性并包含一些额外的逻辑在里面,这些逻辑是指:当这些属性被访问时从数据库获取数据。对用户来说这些是内部机制,用户也不需要关心是否在运行时会有一个dynamic proxy存在。EF take care it!
注意:DbContext有一个配置选项可用来决定是否开启Lazy Loading:DbContext.Configuration.LazyLoadingEnabled。默认属性是True。
为了能够使用dynamic proxies也即支持Lazy Loading,你的类必须符合一些要求。如果这些要求不符合,EF将不会为你的类创建dynamic proxy,而只是简单返回你的类的实例,而这是不能拥有Lazy Loading能力的。
这些要求包括:
- 你的POCO类必须是public并能够被继承的(言下之意么不能是Sealed的)
- 你想要拥有Lazy Loading的导航属性字段必须是virtual的,所以EF才能够重写它们。
Example 2-23
1 private static void TestLazyLoading()
3 using ( var context = new BreakAwayContext())
4 {
5 var query = from d in context.Destinations
6 where d.Name == " Grand Canyon "
7 select d;
8
9 var canyon = query.Single();
10
11 Console.WriteLine( " Grand Canyon Lodging: ");
12 if (canyon.Lodgings != null)
13 {
14 foreach ( var lodging in canyon.Lodgings)
15 {
16 Console.WriteLine(lodging.Name);
17 }
18 }
19 }
20 }
运行以上示例,这段代码是不会打印出Lodging的名称的,原因我想你们也知道了,因为我们最初的Destination类的Lodging导航属性不是virtual的,这就导致EF不能重写这个导航属性,就不能支持Lazy Loading功能。修改很简单:
再次运行程序将会不负所望。
以上代码运行时,EF发送了给数据库两个查询:第一个,当代码调用Single方法时会向数据库查询名称为Grand Canyon的Destination,记住Single方法使用的是SELECT TOP(2)查询来保证只有一个结果。第二个,查询Grand Canyon Destination的所有相关的Lodgings。
Multiple Active Result Sets
在EF对数据库运行查询时,当你第一次从查询读取数据时,它并没有带回所有的数据。每行数据都是按需从数据库加载的。这就意味着你遍历查询结果时,这个查询仍然是活动的并且当你遍历时数据会被从数据库取出来。
Lazy Loading功能在你遍历查询结果时是很常用的。比如,当你查询所有的Destination后,你用foreach遍历查询结果,在这个遍历过程中,你再去访问Lodgings属性,这个属性就会被从数据库延迟加载。这就表示对Lodgings的查询执行时对所有Destingation的查询仍然是活动的。
Multiple Active Result Sets AKA MARS 是SQL Server功能,它允许对同一个数据库连接有多个活动查询。Code First模式下创建一个数据库连接时是默认开启MARS。
如果你未开启MARS同时你的代码试着运行两个活动查询,你将会收到一个异常。这个异常取决于触发第二个查询的操作,但是inner exception将会是“There is already an open DataReader associated with this Command whick must be closed first”。
Lazy Loading的一些缺点
到现在为止,你们会觉得Lazy Loading会非常简单,几乎不用做什么事情,只要你想要什么数据,EF都会帮你加载好,但这也有不好的地方,不正确的使用Lazy Loading会导致很多对数据库的查询执行。比如,你可能加载了50个Destination之后访问每个Destination的Lodgings。这将导致对数据库的51次查询:一次是获得50个Destination,其余50次是查询50个Destination相关的Lodgings。为了防止这种情况发生,就要讲到接下来的另一种加载方式:Eager Loading。
ps:你也可以选择关闭自动开启的Lazy Loading,方法前面已经讲过,关闭之后,就算你的导航属性设置成virtual,Lazy Loading也不起效果。
--这一篇就到这了,下一篇是DbContext查询的最后一篇
转载于:https://www.cnblogs.com/telon/p/4112506.html
DbContext 查询(二)相关推荐
- DbContext 查询(三)
接上一篇<DbContext 查询(二)> Eager Loading 暂且称之为主动加载, 主动加载取决于你要告诉EF你想要加载哪些相关数据进内存,之后EF会在生成的SQL语句中使用J ...
- 【FFmpeg】ffmpeg 命令查询二 ( 比特流过滤器 | 可用协议 | 过滤器 | 像素格式 | 标准声道布局 | 音频采样格式 | 颜色名称 )
FFmpeg 系列文章目录 [FFmpeg]Windows 搭建 FFmpeg 命令行运行环境 [FFmpeg]FFmpeg 相关术语简介 ( 容器 | 媒体流 | 数据帧 | 数据包 | 编解码器 ...
- Linux固件版本查询-二次打包
Linux固件版本查询/二次打包 1.准备环境 Linux安装环境:sudo apt-get install lib32stdc++6 固件下载地址 二次打包固件工具下载 neardi-pack-to ...
- DbContext 查询
使用LINQ to Entities来写查询语句 Entity Framework查询是使用的.NET Framework功能Language Integrated Query,AKA LINQ.LI ...
- 子查询二(在HAVING子句中使用子查询)
HAVING子句的主要功能是对分组后的数据进行过滤,如果子查询在HAVING中表示要进行分组过滤,一般返回单行单列的数据 示例一.查询部门编号,人数,平均工资,并且要求这些部门的平均工资高于公司的平均 ...
- mysql语法子查询_(十四)MySQL语法-子查询(二)
二.放在select后面的子查询 仅仅支持标量子查询 案例1:查询每个部门的员工个数 select d.*,(select count(1) from employees e where e.depa ...
- oracle 表查询(二)
1.使用逻辑操作符号 问题:查询工资高于500或者是岗位为manager的雇员,同时还要满足他们的姓名首字母为大写的J? select * from emp where (sal > 500 o ...
- 【数据库】多表查询二----嵌套查询(子查询)
1.查询至少选修两门课程的男学生姓名 首先看一下学生表和课程选修表: 查询选修大于两门的男生的姓名就涉及到这两张表.当然有些同学会说这表我人工就可以找,但是大数据可不允许你这么猖狂! 首先看一下谁是男 ...
- mybatis高级映射多对多查询(二)
在这篇博客中,我来介绍下mybatis中的多对多查询的案例,在mybatis中,如何使用ResultMap来实现多对多的查询? 案例:一个user可以有很多role,一个role可以有很多entitl ...
最新文章
- open一个页面并关闭父页(小技巧)
- 第十、十一周项目二-存储班长信息的学生类
- 1134. Vertex Cover (25)
- 2.2.1 MySQL基本功能与参数文件管理
- 练习、C# 结构体、冒泡排序
- mysql slave 线程 简书_【MySQL】你真的读懂了slave status吗?
- 牛客题霸 [有关阶乘的两个问题1] C++题解/答案
- ocx控件 postmessage消息会消失_APP控件之二——弹框
- django前端模板循环多个list
- Harris角点检测算法手动实现
- 获取对象属性名的方法 Object.keys() 与 Object.getOwnPropertyNames() 与 for循环
- Python代码书写规范
- 将pandas中object类型转换为int类型
- 提高笔记本无线网络速度
- 七大江河水系--海河
- 数模笔记之“Q值分配法、比例加惯例(D‘Hondt)” matlab代码
- 深度学习模型轻量化(上)
- Python词云实现
- Python实现遗传算法(GA)+支持向量回归机(SVR)
- pandas函数melt的应用
热门文章
- python sys.path用法
- css上下浮动动画效果
- 心理学上的被动_精神分析心理学:人际关系中的被动攻击(1)
- GRAF: Generative Radiance Fields for 3D-Aware Image Synthesis
- UltraScale时钟资源和时钟管理模块
- NNI学习(一)介绍与安装
- pdf修改文字内容怎么修改
- [北力电子] 无人机4G图传数传一体 pixhawk mavlink GSLINK 720P
- 学计算机选择什么编程语言好一些?
- 诊断CAPL自动化(2)—— 封装了诊断发送的通用CAPL函数库