轻松掌握 .NET 技术:深入理解 LINQ
目录
1.LINQ 概述
2.LINQ查询基础
2.1 查询形式
2.2 查询表达式
2.3 标准查询运算符
2.4 LINQ语言特性
2.5 Func委托与匿名方法
2.6 Lambda表达式
3. LINQ实际应用
3.1 新建LINQ to SQL类
3.2 LINQ数据分页查询
1.LINQ 概述
LINQ(Language-Integrated Query,语言集成查询)是微软在.NET Framework 3.5版本引入的新功能,它能够将查询功能直接引入.Net Framework所支持的编程语言中。
如图所示,LINQ主要由LINQ to Objects、LINQ to ADO.NET、LINQ to XML三部分组成。其中:
- LINQ to Objects:可以查询是实现了Ienumerable或Ienumerable<T>的集合,即任意可枚举的集合。
- LINQ to ADO.NET:
- LINQ to SQL:可查询基于关系型数据库的数据,并进行CRUD等操作。
- LINQ to DataSet:对DataSet对象进行检索/过滤/排序等操作。
- LINQ to XML:可对XML结构数据进行查询或操作。
2.LINQ查询基础
2.1 查询形式
- Method syntax 方法语法
通过方法调用的形式调用LINQ查询,这些方法是一组命令式的,标准查询运算符形式的方法,并如SQL语句那样指明调用顺序。
string[] testStrs = new String[] { "This", "is", "my", "home" };
List<string> splitList = testStrs.Where(x => x.Length == 2).ToList();
foreach (string v in splitList) Console.Write(v + "\t");
输出:is my
- Query syntax 查询语法
查询语法是声明式的,类似SQL语句的查询子句,编译器会将其翻译成方法调用的形式。
string[] testStrs = new String[] { "This", "is", "my", "home" };
var splitList = from str in testStrs where str.Length == 2 select str;
foreach (object v in splitList) Console.Write(v + "\t");
输出:is my
2.2 查询表达式
LINQ查询表达式有一个或者多个LINQ查询子句按照一定的规则组成,其中查询子句包括:
-
from ... in:指定数据源和范围变量,这里的范围变量指数据源中的每一个元素
-
where:筛选条件
-
select:指定查询结果的类型和表现形式
-
orderby:对查询结果进行排序(降序/升序)
-
group ... by:对查询结果进行分组
-
into:引用join、group和select子句的结果
-
join ... on:连接多个查询操作的数据源
-
let:引入用于存储查询表达式中子表达式结果的范围变量,这里的范围变量指局部变量
LINQ查询表达式必须包括from子句,并且以from子句开头。下面是一个完整的例子:
List<Scores> scores = GetScores();
List<Student> students = GetStudents();
var list = from stu in students
join score in scores on stu.ID equals score.StudentID
let sum = score.Chinese + score.Math + score.English //定义临时变量sum
where sum / 3 > 60
group stu by new { stu.Age } into result
orderby result.Key.Age descending
select new { Age = result.Key.Age, Count = result.Count() };
2.3 标准查询运算符
LINQ标准查询运算符是有一系列API方法组成,并支持查询任何数组或集合对象。其中,被查询的集合必须实现IEnumberable<T>接口。常用的标准查询运算符(查询方法)有:
-
Select:同select子句
-
Where:同where子句
-
Take:指定要获取的元素个数,同SQL中的top语句
-
Skip:跳过指定的元素个数开始获取
-
Join:对两个对象执行内连接
-
GroupBy:同group by子句
-
OrderBy/ThenBy:OrderBy同orderby子句,ThenBy可对更多的元素进行排序
-
Count:返回集合中元素的个数
-
Sum:返回集合中某一项值得总和
-
Min:返回元素中最小的值
-
Max:返回元素中最大的值
下面是一个完整的例子:
List<Scores> scores = new List<Scores>();
List<Student> students = new List<Student>();
var JoinData = students.Join(scores, //内连接数据源
stu => stu.ID, //内连接外键
score => score.StudentID, //内连接主键
(stu, score) => new { Student = stu, Scores = score }); //指定查询结果
var ResultList = JoinData.Where( W => W.Scores.Chinese > 60)
.GroupBy(G => G.Student.Age)
.OrderByDescending(O => O.Key) //OrderBy默认为升序,OrderByDescending降序
.Select(S => new { Age = S.Key, Count = S.Count()});
2.4 LINQ语言特性
- 隐式类型: var关键字声明变量,编译器通过变量所赋的值来推导数据类型,考虑到可读性,尽量显示指明数据类型。
- 匿名类型:通过var关键字和new关键字实现创建一个匿名类型,例:var obj = new { Name = "大宝哥" };
- 对象初始化器:结合前面两个特性,例:var stu = new Student(){ Name = "大宝哥" };
2.5 Func委托与匿名方法
Func是一个泛型委托,为了方便理解可以将其当做方法的数据类型,通过参数来区分。由于是泛型委托,其参数及返回值由开发者自行决定,注意输入参数可以有0~16个。
int GetSum(int a, int b) { return a + b; } //定义一个求和方法
Func<int, int, int> sum = GetSum; //声明并初始化一个Func委托
int result = sum(2, 3); //调用Func委托
这边查看定义的Func委托sum说明,可以看出声明类型中Func<int,int,int>的3个int参数,前两个泛型参数为调用方法GetSum中的参数类型,最后一个泛型参数为GetSum方法的返回值。
对于上面的显式声明的委托及调用方法,还可以通过前面说的匿名方法来定义委托的回调方法。
Func<int, int, int> sum = delegate (int a, int b)
{
retrun a +b;
}
2.6 Lambda表达式
Lambda表达式是在Func泛型委托和匿名方法的基础上,再进一步对代码进行简化,将委托的回调方法简化到只有参数列表和方法体。“=>”符号左边为表达式的参数列表,右边为方法体,参数列表可包含0到多个参数,声明方式和所要调用的方法形参格式一样。例如,将2.5示例方法中的delegate关键字舍去,可以简化为
Func<int, int, int> sum = (int a, int b) => { retrun a +b; }
若只有一个参数的情况,可以省去参数列表两边的括号
Func<int, int> sum = a => { retrun a * 2; }
注意,若没有参数的情况,需要保留一个空的括号来表示
Func<int, int> sum = () => { retrun 3 * 2; }
3. LINQ实际应用
LINQ本质上就是对数据集合的查询,不论是List、Dictionary等IEnumerable集合,还是XML、DB等,都需要先连接到数据源。这边单独讲下LINQ to SQL,其他的大同小异不做阐述。
3.1 新建LINQ to SQL类
若在Visual Studio中没有找到“LINQ to SQL 类”,则在VS的“工具”菜单中,选择“获取工具和功能”打开Visual Studio Installer
在“单个组件”选项下找到并勾选“LINQ to SQL 工具”后,点击“修改”
继续前面新建LINQ to SQL类的操作,选择“LINQ to SQL类”并创建后,会在项目下生成一个dbml文件
到这一步就要执行连接数据库的操作了,连接DB的操作过程这边不做阐述。
连接完成后打开dbml文件,将所需操作的数据库表拖动至dbml文件页面空白处,这边就会生成实例化操作数据库的上下文Context类,同EF一样通过Context类获取并操作数据。
CityDataContext cdc = new CityDataContext();
var findResult = cdc.City.Where( c => c.Name.Equals("Shanghai"));
这边要注意的是对象关系设计器(O/R设计器)仅支持用于 SQL Server 的.NET Framework 数据提供程序 (System.Data.SqlClient),途中用了MySQL数据库,将左边数据库表拖放至dbml文件中时出现错误。
若不想更改数据库的话,建议使用其他对数据库数据操作的方式,如ODBC,EF等获取数据源后再进行LINQ操作。
3.2 LINQ数据分页查询
/// <summary>
/// 获取页数据方法
/// </summary>
/// <param name="index">要获取的页码</param>
/// <param name="pageSize">每页数据条目数</param>
public void GetPageData(int index, int pageSize)
{
CityDataContext cdc = new CityDataContext();
var findResult = cdc.City.Where( c => c.Name.Length > 3); //获取数据源并筛选结果
//根据传入分页信息参数计算需要跳过的数据条数
int skip = (index - 1) * pageSize;
//跳过计算后的数据条数并获取要获取的数据条数
var pageResult = findResult.Skip(skip).Take(pageSize);
//显示数据操作......
}
推荐阅读
-
轻松掌握 .NET 技术:深入理解 LINQ
-
深入理解C#的LINQ(2):掌握Linq查询语句和查询方法 - 探索Linq查询子句
-
深入理解 .NET Core 中的语言集成查询 (LINQ)
-
深入理解.NET中的LINQ框架(第三部分:优雅地预热LINQ)
-
深入理解.NET中的LINQ框架(第一部分:LINQ的优雅入门)
-
深入理解.NET LINQ框架(四):探究IQueryable和IQueryProvider接口
-
用16个实战练习,深入理解.NET的Linq和Lambda技术
-
C# .NET 实战技巧:深入理解 LINQ 的强大功能
-
一步步学习 LINQ to SQL,轻松掌握 .NET 技术
-
深入理解 .NET Core 中的 Linq 查询运算符(第二部分)