初识Linq to Entity
技术介绍:
LINQ(Language Integrated Query,语言集成查询)是一组用于C#和VB.NET语言的扩展,它允许编写C#或者VB.net代码,以与查询数据库相同的方式操作内存数据。LINQ提供了丰富的类似SQL的查询语法,功能强大且容易上手。下图汇总展示了LINQ技术的官方实现集合:
正如上图所示,LINQ to Entities 是LINQ技术在实体对象模型中的一种实现,它与LINQ to SQL以及LINQ to DataSets一起构成了LINQ to ADO.Net。LINQ to Entities可以生成eSQL,并支持使用LINQ语法对实体框架服务层进行查询。下图展示了LINQ to Entities消费实体数据模型的技术细节:
概念逻辑:
在开始了解LINQ to Entities之前,需要先对.NET Framework 3.5版本后对C#语言的几个扩展特性做一些阐释,这有助于我们更容易、更深刻的理解LINQ to Entities技术的原理与实现。
一、隐式实例化局部变量
.NET Framework 3.5后,实例化局部变量语法有了新的选择:使用“var”关键字隐式类型化:
var user = new User();
隐式类型化语法与JavaScript语言里声明变量的语法类似,它可以一定程度上简化实例化局部变量的语法。但如果隐式实例化的作用仅限于此,那本文不会专门提及。隐式类型化更重要的用途是用于匿名类型的实例化。
需要提醒注意的是,只有局部变量实例化允许使用隐式实例化语法。这表示私有变量实例化无法享受该福利;同时,仅声明变量或实例化对象为null也是不允许使用隐式实例化语法的。
二、对象初始化
对象初始化指在实例化对象时,即可对对象的属性进行赋值:
1 var user = new User()
2 {
3 ID = Guid.NewGuid(),
4 Account = "Apollo"
5 };
三、匿名类型
LINQ to Entities的Select可以将实体类型投影为匿名类型,所以有必要对匿名类型作简单介绍。匿名类型指的是不显示声明类型的细节,而是根据上下文环境需要,临时声明满足需要的类型。由于该类型是临时需要的,所以不必为之命名。匿名类型的声明语法如下:
var user = new { ID = Guid.NewGuid(), Name = "Apollo" };
四、扩展方法
扩展方法是微软为扩展已有框架而创造的一个语法糖,.NET Framework 3.5就是通过很多扩展方法实现了对.NET Framework 2.0的升级扩展的。扩展方法很神奇,被扩展的对象可以不知道扩展方法的存在,就能在行为上得到扩展。扩展方法也很蹩脚,如果使用者不知情,很可能不知道对象具有扩展行为;抑或知道有扩展方法,但是不知道要引用哪个扩展库以使其支持扩展的行为。扩展方法的语法如下:
1 public static class UserExt
2 {
3 public static void Drink(this User user, object water)
4 {
5 …
6 }
7 }
五、Lambda表达式
Lambda表达式是由委托以及匿名方法发展而来的,它可将表达式或代码块(匿名方法)赋给一个变量,从而以最少量的输入实现编码目的。Lambda表达式一般配合IEnumerable<T>的静态扩展方法使用,完成对象集合的快捷查询操作。Lambda表达式的语法如下:
var user = db.Users.FirstOrDefault(o => o.Account == "Apollo");
六、标准查询操作符
System.Linq.Enumerable静态类声明了一套标准查询操作符(Standard Query Operators,SQO)方法集合。标准查询操作符的语法和标准SQL很相似,这不是偶然,而是微软有意为之,以使熟悉SQL的程序员们更容易上手。标准查询操作符的基本语法如下:
1 using (var db = new EntityContext())
2 {
3 var roles = from o in db.Users
4 where o.Account == "Apollo"
5 select o.Roles;
6 …
7 }
标准查询操作符和Lambda表达式的关系非常密切。编译器会将上诉表达式转化为下列以Lambda表达式为参数的显式扩展方法调用序列:
1 using (var db = new EntityContext())
2 {
3 var roles = db.Users.Where(o => o.Account == "Apollo").Select(o => o.Roles);
4 }
标准查询操作符
接下来,将针对数据查询操作中常用的条件查询、投影、分区、排序、分组、集合、元素、量词和聚集等标准查询操作符进行分类介绍。
一、条件操作符
条件操作符Where类似于SQL中的WHERE子句,用于实现条件查询。下列扩展方法表达式查询满足条件“角色不为空”的用户集合:
var user = db.Users.Where(o => o.Roles != null);
对应的标准查询操作符表达式为:
1 var users = from o in db.Users
2 where o.Roles != null
3 select o;
二、投影操作符
投影操作符Select类似于SQL中的SELECT子句,将对象投影为一个匿名类型实例,用于控制指定查询迭代器显示或者处理的对象属性。另外,需要注意的是,扩展方法表达式中的Select操作符并非必须的,省略模式下,会返回完整的被投影对象。下列扩展方法表达式将用户的帐号和密码信息投影为一个匿名类型:
var users = db.Users.Select(o => new { o.Account, o.Password });
对应的标准查询操作符表达式为:
1 var users = from o in db.Users
2 select new { o.Account, o.Password };
三、分区操作符
分区操作符实现对象的分区操作。其中,Take操作符类似于SQL中的TOP操作符,下列扩展方法表达式返回前5个用户对象:
var users = db.Users.OrderBy(o => o.Roles.Count).Take(5);
Skip操作符用于跳过指定个数对象并返回序列中的剩余对象,下列扩展方法表达式返回除前10个用户外的剩余用户:
var users = db.Users.OrderBy(o => o.Roles.Count).Skip(10);
TakeWhile操作符用于返回条件表达式值为真时的相邻元素集合,下列扩展方法表达式返回第一个拥有3个角色的用户之前的所有用户集合:
var users = db.Users.OrderBy(o => o.Roles.Count).TakeWhile(o => o.Roles.Count == 3);
SkipWhile操作符用于跳过条件表达式值为真时的元素,并返回剩下的元素集合,下列扩展方法表达式返回第一个拥有3个角色的用户之后的所有用户集合:
var users = db.Users.OrderBy(o => o.Roles.Count).SkipWhile(o => o.Roles == 3);
四、排序操作符
排序操作符实现对象的排序功能,包括OrderBy、OrderByDescending、ThenBy、ThenByDescending和Reverse五个操作符。其中OrderBy操作符实现对象的升序排列,相当于SQL中的ORDER BY ASC子句,下列扩展方法表达式实现用户按拥有的角色数进行升序排列:
var users = db.Users.OrderBy(o => o.Roles.Count);
对应的标准查询操作符表达式为:
1 var users = from o in db.Users
2 orderby o.Roles.Count
3 select o;
OrderByDescending操作符实现对象的降序排列,相当于SQL中的ORDER BY DESC子句,下列扩展方法表达式实现用户按拥有的角色数进行降序排列:
var users = db.Users.OrderByDescending(o => o.Roles.Count);
对应的标准查询操作符表达式为:
1 var users = from o in db.Users
2 orderby o.Roles.Count descending
3 select o;
ThenBy、ThenByDescending和Reverse操作符只能针对IOrderedEnumerable接口对象使用,所以一般紧跟在OrderBy/OrderByDesending操作符方法后使用。ThenBy操作符由编译器翻译为对OrderBy操作符的再次调用;ThenByDescending操作符由编译器翻译为对OrderByDescending操作符的再次调用;Reverse操作符实现对象的排序反向。这里不再一一举例。
五、分组操作符
分组操作符GroupBy类似于SQL中的GROUP BY子句,实现对象的分组操作。下列扩展方法表达式实现用户对象按拥有的角色数量进行分组:
var users = db.Users.GroupBy(o => o.Roles.Count);
对应的标准查询操作符表达式为:
1 var users = from o in db.Users
2 group o by o.Roles.Count into g
3 select new { RoleCount = g.Key, Group = g };
六、集合操作符
集合操作符包括Distinct、Union、intersect和Except四个操作符,除Distinct外,其他三个操作符都可将两个序列组合成一个序列。Distinct操作符类似于SQL中的DISTINCT关键字,用于删除序列中具有重复值的对象。下列扩展方法表达式实现将用户角色中的重复角色删除功能:
var roles = user.Roles.Distinct();
Union操作符类似于SQL中的UNION关键字,用于求具有同样结构的两个序列的并集。下列扩展方法表达式实现将用户1和用户2所拥有的角色组合成一个角色集合,并排除其中重复的角色:
var roles = user1.Roles.Union(user2.Roles);
Intersect操作符类似于SQL中的INTERSECT关键字,用于求具有同样结构的两个序列的交集。下列扩展方法表达式返回用户1和用户2都具有的角色集合:
var roles = user1.Roles.Intersect(user2.Roles);
Except操作符类似于SQL中的EXCEPT关键字,用于返回第一个序列中有、但第二个序列中没有的对象集合。下列扩展方法表达式返回用户1拥有,而用户2没有的角色集合:
var roles = user1.Roles.Except(user2.Roles);
七、元素操作符
元素操作符包括两组操作符,分别是用于从一个IEnumerable<T>序列中返回满足条件的单个对象或无满足条件对象时抛异常的First、Last和Single操作符,以及返回满足条件的单个对象或无满足条件对象时返回空对象的FirstOrDefault、LastOrDefault和SingleOrDefault操作符。其中First和FirstOrDefault操作符用于返回第一个满足条件的对象。下列扩展方法表达式返回第一个拥有三个角色的用户:
var user = db.Users.FirstOrDefault(o => o.Roles.Count == 3);
Last和LastOrDefault操作符用于返回最后一个满足条件的对象。下列扩展方法表达式返回最后一个拥有三个角色的用户:
var user = db.Users.LastOrDefault(o => o.Roles.Count == 3);
Single和SingleOrDefault操作符用于返回满足条件的序列中的唯一元素,如果序列中包含不止一个元素,将会抛异常。下列扩展方法表达式返回帐号为“Apollo”的唯一用户,如果有多个用户帐号都为“Apollo”,则抛异常:
var user = db.Users.SingleOrDefault(o => o.Account == "Apollo");
八、量词操作符
量词操作符包括 Any、All和Contains三个操作符,用于检查序列中是否有一些对象或所有对象满足条件。其中,Any操作符用于检查序列中是否有任何一个对象满足条件。下列扩展方法表达式当有任何一个用户拥有三个角色时返回真,否则返回假:
var result = db.Users.Any(o => o.Roles.Count == 3);
All操作符用于检查序列中是否所有对象均满足条件。下列扩展方法表达式当所有用户均拥有三个角色时返回真,否则返回假:
var result = db.Users.All(o => o.Roles.Count == 3);
Contains操作符用于检查序列中是否包含指定的对象。下列扩展方法表达式当集合中包含用户1则返回真,否则返回假:
var result = db.Users.Where(o => o.Roles.Count == 3).Contains(user1);
九、聚集操作符
聚集操作符包括Count、Min、Max、Sum和Average等多个操作符,用于对对象集合进行统计计算。其中,Count操作符类似于SQL中的COUNT关键字,用于计算序列中满足条件的对象个数。下列扩展方法表达式返回拥有3个角色的用户数量:
var result = db.Users.Count(o => o.Roles.Count == 3);
Min操作符类似于SQL中的MIN关键字,用于返回按条件计算的最小值。下列扩展方法表达式返回拥有最少角色数量的用户所拥有的角色数量:
var result = db.Users.Min(o => o.Roles.Count);
Max操作符类似于SQL中的MAX关键字,用于返回按条件计算的最大值。下列扩展方法表达式返回拥有角色数量最多的用户所拥有的角色数量:
var result = db.Users.Max(o => o.Roles.Count);
Sum操作符类似于SQL中的SUM关键字,用于返回按条件计算的总数。下列扩展方法表达式返回已赋予用户的所有角色总数:
var result = db.Users.Sum(o => o.Roles.Count);
Average操作符类似于SQL中的AVERAGE关键字,用于返回按条件计算的平均值。下列扩展方法表达式,用于返回用户所拥有的角色平均数:
var result = db.Users.Average(o => o.Roles.Count);
总结
本文首先给出了LINQ技术的官方实现集合,以及LINQ to Entities实现的技术细节;然后概要介绍了与LINQ to Entities相关的几个基本概念;最后对LINQ to Entities常用的标准查询操作符的使用进行了分类介绍,从中也可以看出LINQ to Entities和SQL的功能基本是一一对应的。
文章写得比较通俗易懂也相对全面,更多的是给自己做一些记录,方便自己查看。
初识Linq to Entity的更多相关文章
- Linq to Entity经验:表达式转换
http://www.cnblogs.com/ASPNET2008/archive/2012/10/27/2742434.html 最近一年的项目,我主要负责一些小型项目(就是指企业内部的小项目),在 ...
- EF架构~linq to entity的随机排序问题
回到目录 对于从linq to sql迁移过来的开发者,对随机排序不会感到陌生,直接为datacontext添加一个方法再配合反射就可以实现随机排序了,代码如下: /// <summary> ...
- EF架构~在Linq to Entity中使用日期函數
回到目录 眾所周知,在linq to entity的查询语句中,不允许出现ef不能识别的关键字,如Trim,Substring,TotalDays等.net里的关键字,在EF查询里都是不被支持的,它的 ...
- Linq to Entity中连接两个数据库时要注意的问题
Linq to Entity中连接两个数据库时要注意的问题 今天大学同学问了我一个问题,Linq to Entity中连接两个数据库时,报错“指定的 LINQ 表达式包含对与不同上下文关联的查询的引用 ...
- C# Linq to Entity 多条件 OR查询
技术背景:框架MVC,linq to Entity 需要一定的lambda书写能力 问题:在简单的orm中完成一些简单的增删查改是通过where insert delete update 完成的,但是 ...
- Linq to Entity 多条件 OR查询
技术背景:框架MVC,linq to Entity 需要一定的lambda书写能力 问题:在简单的orm中完成一些简单的增删查改是通过where insert delete update 完成的,但是 ...
- Linq学习<三> linq to entity
之前一直用sql选择出数据放在一个集合中,然后再用Linq或者lambda去操作数据,今天学了Linq to entity 才知道原来linq产生是为了Entity.也就是EDM(实体数据模型) 关于 ...
- Linq to Entity 时间差作为筛选条件产生的问题
前言 在使用 Linq to Entity 的時候,會把之前 Linq to SQL 的想法就帶進去,寫好之後編譯也都不會出錯,但是實際上在跑的時候就會出現錯誤訊息了,這點真的要注意了.這次我遇到問題 ...
- 深入调研Linq to Objects Join Linq to Entity
最近工作中遇到数据库组合查询带来的一些问题,因此有必要调研一下Linq to Objects Join Linq to Entity.参考一些网友的代码案例,深入实践了一下使用EntityFramew ...
随机推荐
- Oracle中Date和Timestamp的区别
Date和Timestamp精度不一样: 01)Timestamp精确到了秒的小数点(如:2018-11-13 16:40:03.698): 02)Date只精确到整数的秒(如:2018-11-13 ...
- 大数据中Linux集群搭建与配置
因测试需要,一共安装4台linux系统,在windows上用vm搭建. 对应4个IP为192.168.1.60.61.62.63,这里记录其中一台的搭建过程,其余的可以直接复制虚拟机,并修改相关配置即 ...
- virtualBox linux操作系统centos 挂载光盘
virtualBox虚拟机linux centos 挂载光盘 第一步: 放入光盘 第二步: 挂载光驱 (/dev/cdrom) 1) 创建挂载点 mkdir /mnt/media 2) 挂载 moun ...
- centos7 安装jenkenis
安装Java 看到当前系统Java版本的命令: java -version 如果显示Java版本号,说明已经正确安装,如果显示没有该命令,需要安装Java: sudo yum install java ...
- LR测试报告分析 -详解
1. 结果摘要 LoadRunner进行场景测试结果收集后,首先显示的该结果的一个摘要信息,如下图所示.概要中列出了场景执行情况.“Statistics Summary(统计信息摘要)”.“Trans ...
- 关于python中的tkinter模块
python2.7和python3.6中的tkinter是两个包,不会自动升级,假如在fedora28做开发的话, 错误:用import Tkinter /import tkinter /import ...
- 在HTML中引用JavaScript中的变量
和上次的代码几乎一样,但这次是引用已经写好的变量.主要功能和用法如下: document对象的getElementId方法得到HTML元素. HTML元素的value属性可以用来设置变量的值. 02. ...
- 如何在window服务器上搭建一个能代替ftp的传输工具
通常对于服务器上的文件管理和数据传输都是利用ftp来实现,但随着存储技术的发展,数据资产的存储规模和复杂程度不断提高,传统的ftp传输显得有笨重.今天给大家介绍一款能够取代ftp的在线文档管理软件—— ...
- docker node项目 连接mongodb
在弄docker部署node项目的时候遇到了连接mongdb的问题,记录一下问题解决办法 一.Docker 安装 MongoDB 1.查找Docker Hub上的mongo镜像 [root@VM_49 ...
- linux一切皆文件之文件描述符(一)
一.知识准备 1.在linux中,一切皆为文件,所有不同种类的类型都被抽象成文件.如:普通文件.目录.字符设备.块设备.套接字等 2.当一个文件被进程打开,就会创建一个文件描述符.这时候,文件的路径就 ...