Linq、延迟加载、直接加载
1、集合常用扩展方法
Where、Max、Min、OrderBy、
Select、//投影后的IEnumerable对象可以通过,AsQueryable转换数据类型
First、FirstOrDefault
Single、SingleOrDefault
Any():判断集合是否包含元素,返回值 bool,一般比 Coun()>0 效率高。 Any 还可以指 定 条 件 表 达 式 。
bool b = list.Any(p => p.Age > 50);
等 价 于 bool b =list.Where(p=>p.Age>50).Any();
Distinct(),剔除完全重复数据
排序:升序 list.OrderBy(p=>p.Age);
降序 list.OrderByDescending(p=>p.Age)。
指定多个排序规则OrderBy: list.OrderByDescending(p=>p.Age).ThenBy(p=>p.Salary),
也支持 ThenByDescending()。注意这些操作不会影响原始的集合数据。
Skip(n)跳过前 n 条数据;
Take(n)获取最多 n 条数据,如果不足 n 条也不会报错。
常用来分页获取数据。 list.Skip(3).Take(2)跳过前 3 条数据获取 2 条数据。
Except(items1)排除当前集合中在 items1 中存在的元素
Union(items1)把当前集合和 items1 中组合。
Intersect(items1) 把当前集合和 items1 中取交集。
分组:
IEnumerable<IGrouping<int,Person>> items=list.GroupBy(g=>g.Age);
foreach(IGrouping<int,Person> item in items){
Console.WriteLine("Key="+item.Key);
foreach(Person p in item){
Console.WriteLine(p);
}
} Console.ReadKey();

SelectMany:把集合中每个对象的另外集合属性的值重新拼接为一个新的集合
foreach(var s in teachers.SelectMany(t => t.Students))
{
Console.WriteLine(s);//每个元素都是 Person
}
注意不会去重,如果需要去重要自己再次调用 Distinct()
Join 可以实现和数据库一样的 Join 效果,对有关联关系的数据进行联合查询
下面的语句查询所有 Id=1 的狗,并且查询狗的主人的姓名。
var result = dogs.Where(d => d.Id > 1).Join(masters, d => d.MasterId, m => m.Id,
(d,m)=>new {DogName=d.Name,MasterName=m.Name});
Linq表达式原理:
在写Linq表达式并完成IQueryable<>对象创建的初始化过程:声明元素类型,建立Expression对象,把Linq表达式进行解析,拆分,解析成一个表达式树,也就是对Lambda表达式的解析过程,
Expression对象以表达式树结构存储解析结果。最后根据LINQ表达式类型给IQueryableProvider属性赋值。在需要读取数据的时候Provider属性就去解析Expression表达式树执行查询返回。
2、延迟加载
解析:被延迟加载的类或者集合大多都被virtual修饰;被修饰后,可以使用当前类的导航属性。eg
public class Students{
public int id{get;set;}
public string name{get;set;}
public virtual Class class{get;set;}
}
Students
using(MyContext ctx=new MyContext())
{
var s=ctx.Students.First();
Console.WriteLine(s.Name)
//为什么能够.出来Class就是因为在Students时对class修饰了virtual
//如果删掉virtual则会报未将对象引用对象的实例
var c=s.Class;
Console.WriteLine(c.Name) }
在不配置virtual时追踪查看linq编译的sql语句是

如果去掉virtual执行的结果是只查询了Students表并且程序到c.Name时报错

不去掉virtual则会把关联属性Class表也查一次,在程序执行到c.Name时执行的
这叫“延迟加载”,只有用到关联的对象的数据,才会再去执行语句。
注意:延迟加载只在关联对象属性上,普通属性没这个东西。
注意:启用延迟加载需要配置如下两个属性(默认就是 true,因此不需要去配置,只要别手贱设置为false 即可)
context.Configuration.ProxyCreationEnabled = true;
context.Configuration.LazyLoadingEnabled = true;
using(MyContext ctx=new MyContext()){
var s=ctx.Students.First();
//被virtual修饰时
//通过反射知道s指向的对象并不是Student对象,而是s的父类才是Student对象
//所以可以写成Student s=ctx.Students.First();
//父类类型的变量可以指向子类类型的对象
Console.WriteLine(s.GetType().BaseType());
//去掉virtual时
//可以看到s直接指向了student对象
Console.WriteLine(s.Name)
var c=s.ClassName;
Console.WriteLine(c.Name)
}
那么,为什么呢?Virtual实现了什么呢?
EF内部动态帮程序生成了实体类对象的子类,然后override了这些virtual属性。
实现:
public class StudentProxy:Student
{ private Class clz;
public override Class Class{
get
{
if(this.clz==null){
this.clz= ....//这里是从数据库中加载 Class 对象的代码
}
return this.clz;
}
}
}
强调:如果要使用延迟加载,类必须是 public,关联属性必须是 virtual
优点: 用到的时候才加载,没用到的时候才加载,因此避免了一次性
加载所有数据,提高了加载的速度。
缺点:如果不用延迟加载,就可以一次数据库查询就可以把所有数据都取出来(使用 join 实现),用了延迟加载就要多次执行数据库操作,提高了数据库服务器的压力。
因此:如果关联的属性几乎都要读取到, 那么就不要用延迟加载; 如果关联的属性只有较小的概率(比如年龄大于 7 岁的学生显示班级名字,否则就不显示)则可以启用延迟加载。
这个概率到底是多少是没有一个固定的值,和数据、 业务、技术架构的特点都有关系,这是需要经验和直觉,也需要
测试和平衡的。
注意:启用延迟加载的时候拿到的对象是动态生成类的对象,是不可序列化的,因此不能直接放
到进程外 Session、 Redis 等中,要转换成 DTO(后面讲)再保存。
如遇上述直接加载的情况,
可以将类中引用其他类的属性声明过来不要使用类
public class StudentModel{
public int id{get;set;}
public string name{get;set;}
public string className{get;set;}
}
StudentModel
3、直接加载
Include(): var s = ctx.Students.Include("Class").First();
Include("Class")的意思是直接加载 Student 的 Class 属性的数据。
注意:只有关联的对象属性才可以用 Include,普通字段不可以直接写"Class"可能拼写错误;
如果用 C#6.0,可以使用 nameof 语法解决问这个问题:
var s = ctx.Students.Include(nameof(Student.Class)).First();
也可以 引用using System.Data.Entity;
var s = ctx.Students.Include(e=>e.Class).First(); 推荐这种做法。
如果有多个属性需要一次性加载,也可以写多个 Include:
var s = ctx.Students.Include(e=>e.Class) .Include(e=>e.Teacher).First();
如果 Class 对象还有一个 School 属性,也想把 School 对象的属性也加载,就要:
var s = ctx.Students.Include("Class").Include("Class. School").First(); 或者更好的
var s = ctx.Students.Include(nameof(Student.Class))
.Include(nameof(Student.Class)+"."+nameof(Class.School)).First();
4、延迟加载的坑
①DbContext 销毁后就不能再延迟加载,因为数据库连接已经断开
下面的代码最后一行会报错:
Student s;
using (MyDbContext ctx = new MyDbContext())
{
s = ctx.Students.First();
}
Console.WriteLine(s.Class.Name);
解决方法:
1) 用 Include,不延迟加载(推荐)
Student s;
using (MyDbContext ctx = new MyDbContext())
{
s = ctx.Students.Include(t=>t.Class).First();
}
Console.WriteLine(s.Class.Name);
2) 关闭前把要用到的数据取出来
Class c;
using (MyDbContext ctx = new MyDbContext())
{
Student s = ctx.Students.Include(t=>t.Class).First();
c = s.Class;
}
Console.WriteLine(c.Name);
②多个取数据操作占用同一数据源
已有打开的与此 Command 相关联的 DataReader,必须首先将它关闭。
foreach(var s in ctx.Students)
{
Console.WriteLine(s.Name);
Console.WriteLine(s.Class.Name);
}
因为 EF 的查询是“延迟执行”的,只有遍历结果集的时候才执行 select 查询,而由于延迟加载的存在到 s.Class.Name 也会再次执行查询。 ADO.Net 中默认是不能同时遍历两个DataReader。因此就报错。
解决方式:
1) 执行一下 ToList(),因为 ToList()就遍历然后生成 List:
foreach(var s in ctx.Students.ToList())
{
Console.WriteLine(s.Name);
Console.WriteLine(s.Class.Name);
}
2)推荐做法: 用 Include 预先加载:
foreach(var s in ctx.Students.Include(e=>e.Class))
{
Console.WriteLine(s.Name);
Console.WriteLine(s.Class.Name);
}
5、IQueryable接口的操作
interface IQueryable<out T> : IEnumerable<T>, IQueryable, IEnumerable
IQueryable类型的返回值的数据在函数生成sql语句时做处理
IEnumerable类型的返回值的函数生成的sql语句后,在内存中处理
IEnumerable在内存中执行操作性能很低
下面的代码会报错:
using (MyDbContext ctx = new MyDbContext())
{
BaseDAO<Student> dao = new BaseDAO<Student>(ctx);
foreach(var s in dao.GetAll()){
Console.WriteLine(s.Name);
Console.WriteLine(s.Class.Name);
}
}
原因是什么?怎么 Include?
需要 using System.Data.Entity;
using (MyDbContext ctx = new MyDbContext())
{
BaseDAO<Student> dao = new BaseDAO<Student>(ctx);
foreach(var s in dao.GetAll().Include(t=>t.Class))
{
Console.WriteLine(s.Name);
Console.WriteLine(s.Class.Name);
}
}
有两个版本的 Include、 AsNoTracking:
1) DbQuery 中的: DbQuery<TResult> AsNoTracking()、 DbQuery<TResult> Include(string path)
2) QueryableExtensions 中 的 扩 展 方 法 : AsNoTracking<T>(this IQueryable<T> source) 、
Include<T>(this IQueryable<T> source, string path)、 Include<T, TProperty>(this IQueryable<T>
source, Expression<Func<T, TProperty>> path)
DbSet 继承自 DbQuery; Where()、 Order、 Skip()等这些方法返回的是 IQueryable 接口。
如 果 在 IQueryable 接 口 类 型 的 对 象 上 调 用 Include 、 AsNoTracking 就 要 using System.Data.Entity
Linq、延迟加载、直接加载的更多相关文章
- Android中ViewPager+Fragment取消(禁止)预加载延迟加载(懒加载)问题解决方案
转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53205878本文出自[DylanAndroid的博客] Android中Vie ...
- jQuery延迟加载(懒加载)插件 – jquery.lazyload.js-Web前端(W3Cways.com) - Web前端学习之路
Lazy Load 是一个用 JavaScript 编写的 jQuery 插件. 它可以延迟加载长页面中的图片. 在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动到它们所在的位置. 这与图片预 ...
- jquery.lazyload.js图片延迟加载(懒加载)--转载
一.插件介绍 jquery.lazyload.js 是一个用 JavaScript 编写的jQuery 插件. 它可以延迟加载长页面中的图片. 在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动 ...
- 使用jquery插件实现图片延迟加载--懒加载技术
原文链接:http://www.cnblogs.com/lei2007/archive/2013/05/31/3110725.html 感谢作者.以下为原文,备忘仅供自己学习. 第一:lazyLoad ...
- jQuery延迟加载(懒加载)插件 – jquery.lazyload.js
引入:<script type="text/javascript" src="${base}/resources/shop/js/jquery.lazyload.j ...
- Swift中的延迟加载(懒加载)
Swift方式的延迟加载 而在Swift中,你只需一行代码即可实现此机制: lazy var players = String[]() 简单.简洁,直入主题. 但你得记住,你必须使用var关键字来定义 ...
- Hibernate之加载策略(延迟加载与即时加载)和抓取策略(fetch)
假设现在有Book和Category两张表,表的关系为双向的一对多,表结构如下: 假设现在我想查询id为2的那本书的书名,使用session.get(...)方法: Session session=H ...
- entity framework 数据加载三种方式的异同(延迟加载,预加载,显示加载)
三种加载方式的区别 显示加载: 显示加载
- Entity Framework 4.1 : 贪婪加载和延迟加载
这篇文章将讨论查询结果的加载控制. EF4.1 允许控制对象之间的关系,当我们进行查询的时候,哪些关系的数据将会被加载到内存呢?所有相关的对象都需要吗?在一些场合可能有意义,例如,当查询的实体仅仅拥有 ...
- [NHibernate]立即加载
目录 写在前面 文档与系列文章 立即加载 一个例子 总结 写在前面 上篇文章介绍了nhibernate延迟加载的相关内容,简单回顾一下延迟加载,就是需要的时候再去加载,需要的时候再向数据库发出sql指 ...
随机推荐
- input[type=radio]选中的样式变化
input[type=radio]:hover{ border: 2px solid #D0D0D0; } input[type=radio]:focus{ border: 2px solid #1B ...
- Kaggle竞赛顶尖选手经验汇总
What is your first plan of action when working on a new competition? 理解竞赛,数据,评价标准. 建立交叉验证集. 制定.更新计划. ...
- timeval的时间转换成毫秒之后多大的数据类型可以装下
struct timeval { long tv_sec; /*秒*/ long tv_usec; /*微秒*/ }; 秒的定义为long,为了防止溢出,转换成毫秒之后保存在long long中
- jquery ajax 全介绍
下面是Jquery中AJAX参数详细列表: 参数名 类型 描述 url String (默认: 当前页地址) 发送请求的地址. type String (默认: "GET") 请求 ...
- 洛谷P1914 小书童——密码
题目背景 某蒟蒻迷上了"小书童",有一天登陆时忘记密码了(他没绑定邮箱or手机),于是便把问题抛给了神犇你. 题目描述 蒟蒻虽然忘记密码,但他还记得密码是由一串字母组成.且密码是由 ...
- CentOS 笔记(三) 目录结构
理解CentOS 目录结构 首次登录进入,应该是进入了,run文件夹 通过 cd ../ 进入了,最根节点 通过 ls 显示全部文件夹 通过 pwd 查看当前目录 参考: https://www ...
- Dict字典的操作
字典的操作 1.字典新增键值对 已存在内容的字典新增 alient_0 = {"color":"green",position:10} alient_0[&qu ...
- JAVA基础总结【面试】
前言 近间陆续面试了不少的求职的前(JAVA).后(WEB)端开发人员,包括实习生.应届毕业生.一两年工作经验的.也有三四年工作经验的,也算见过了比较多的开发人员,想在这里做个总结,本次主要讲一讲面试 ...
- nodejs-Module
nodejs的打包程序 模块:实现特定功能的文件 1.通过require引入模块 2.模块中的功能(变量,函数),通过赋给exports对象的某个属性提供给调用者使用 1 2 function sum ...
- BA-楼宇自控系统设计论文(转载)潘翌庆 元晨
楼宇自控系统设计 潘翌庆 元晨 一.概述 楼宇自控系统(Building Automation System-BAS)是智能建筑中不可缺少的重要组成部分,在智能建筑中占有举足轻重的地位.它对建筑物内部 ...