LINQ查询
LINQ能够解决的问题
.NET平台开发中一直存在的情况:
-
面向对象编程语言与数据库访问方法长期分离,以嵌入式方式开发
嵌入式SQL语句没有任何提示,很容易出错
-
编程语言中的数据类型与数据库中的数据类型完全是两套体系
例如:C#中字符串string在SQL中使用varchar/Nvarchar/char来表示
-
SQL和XML都有各自的查询语言,而对象没有自己的查询语言
例如:要从一个List< T>集合或者数组中找到符合要求的元素非常困难。
LINQ查询重点解决以上问题
LINQ是什么
LINQ(Language Integrated Query)
语言集成查询
- 是微软公司提供的一项新技术,能够将查询功能直接引入到C#、VB.NET等编程语言中
- 查询操作可以通过编程语言自身来表示,而不是嵌入字符串SQL语句
LINQ的组成
- LINQ to Objects 主要负责对象的查询
- LINQ to XML 主要负责XML的查询
- LINQ to ADO.NET 主要负责数据库的查询
- LINQ to SQL (目前已经没人使用)
- LINQ to DataSet
- LINQ to Entities (重点学习)
LINQ的命名空间
- System.Linq;该命名空间已经由系统自动引入
- 因此微软默认建议多使用Linq技术查询
LINQ的组成架构
不采用LINQ技术的查询
static void Main(string[] args)
{
//1.从数组中拿到所有的偶数
int[] nums = { 1, 22,18,2, 3, 5, 7, 6, 8, 9, 23, 12 };
List<int> list = new List<int>();
foreach (int item in nums)
{
if (item%2==0)
{
list.Add(item);
}
}
list.Sort();
list.Reverse();
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
Console.ReadLine();
}
采用LINQ技术进行查询
int[] nums = { 1, 22, 18, 2, 3, 5, 7, 6, 8, 9, 23, 12 };
var list = from item in nums
where item % 2 == 0
orderby item descending
select item;
foreach (var item in list)
{
Console.WriteLine(item);
}
LINQ查询方法
获取数据方法
扩展方法:Select()
- Select()是一个泛型扩展方法
- Select()方法使用的时候,要求传递一个委托实例(委托实例就是一个方法)
Select()方法应用
int[] nums = { 1, 22, 18, 2, 3, 5, 7, 6, 8, 9, 23, 12 };
var list = nums.Select(item=>item*2);
foreach (var item in list)
{
Console.WriteLine(item);
}
- Select()方法里面是一个Lambda表达式
- 返回结果是一个迭代[隐式循环]器(Iterator)
- 不论数组还是集合都可以使用Select()扩展方法
筛选数据方法
扩展方法:Where()
- Where()方法是一个扩展泛型方法
- Where()方法使用的时候要求传递一个委托实例,但该实例是一个判断条件,因此返回的类型必须是bool类型
Where()方法应用
var list = nums.Where(item => item % 2 != 0).Select(item=>item*2);
foreach (var item in list)
{
Console.WriteLine(item);
}
“链式编程”:在一个方法完成之后可以继续紧接着调用方法
注意:链式编程在每个环节之后能否继续链式要考虑这个环节的结果是否是集合
排序数据方法
扩展方法:OrderBy()
- OrderBy()是一个扩展方法
- OrderBy()里面的参数要求传递一个排序字段,默认按照升序排序
- 如果需要降序排列可以使用OrderByDescending方法
OrderBy()方法应用
var list = nums.Where(item => item % 2 == 0)
.Select(item => item * 2)
.OrderBy(item=>item);
foreach (var item in list)
{
Console.WriteLine(item);
}
string[] arry = { "张三","李四","王五","赵六","陈七","宋八","杜九"};
var list = arry.Select(item => item).OrderBy(item => item.Substring(1,1));
foreach (var item in list)
{
Console.WriteLine(item);
}
分组数据方法
扩展噶GroupBy()
- GroupBy()是一个扩展方法
- GroupBy()里面的参数要求传递一个分组字段
GroupBy()方法应用
var list = arry.Select(item => item).GroupBy(item => item.Substring(0, 1));
foreach (var item in list)
{
Console.WriteLine("---------------------------");
Console.WriteLine("分组字段:" + item.Key);
foreach (var i in item)
{
Console.WriteLine(i);
}
}
LINQ查询形式
查询步骤:获取数据源、定义查询、执行查询
结论:
- 定义查询后,查询并没有立即执行,而是直到需要枚举结果(遍历)的时候才真正执行查询工作
- 这种方式称为“延迟执行(deferred execution)”
可以使用“聚合扩展方法”,返回单一结果,强制查询立即执行
LINQ查询的两种形式
查询方法方式
Method Syntax
- 主要利用System.Linq.Enumerable类中定义的扩展方法和Lambda表达式方式查询
- 在此之前所用的查询都是这种方法
查询语句方法
Query Syntax
- 一种更接近SQL语法的查询方式,可读性更好
- 查询语句最后还是要被翻译成查询方法
string[] arry = { "张三","李四","王五","赵六","陈七","宋八","杜九", "张无极", "李世民", "王超", "赵四" , "张三丰", "李渊", "王子", "赵敏" };
var list = from item in arry
group item by item.Substring(0,1);
foreach (var item in list)
{
Console.WriteLine("---------------------------");
Console.WriteLine("分组字段:" + item.Key);
foreach (var i in item)
{
Console.WriteLine(i);
}
}
和查询方法的查询结果完全一致
两种结果比较
-
CLR本身并不理解查询语句,他只理解查询方法
-
编译器只负责编译时将查询语句翻译为查询方法
-
大部分查询方法都有对应的查询语句形式:select()对应select、OrderBy()对应order…by…
-
部分查询方法目前在C#中还没有对应的语句,例如Count()和Max()等,这时只能采用以下方案
- 查询方法
- 查询方法+查询方法的混合方式
一般情况下,建议使用可读性强的查询语句查询
LINQ查询子句
查询表达式
- 是一种用查询语法表示的表达式,由一组类似于SQL的语法编写的句子组成
- 每一个子句可以包含一个或多个C#表达式
int[] nums = { 1, 22, 18, 2, 3, 5, 7, 6, 8, 9, 23, 12 };
var list = from item in nums
where item % 2 != 0
orderby item * 2 descending
select item * 2;
foreach (var item in list)
{
Console.WriteLine(item);
}
LINQ查询表达式必须以from子句开头,并且必须以select或者group子句结束,中间可以添加多个子句
查询子句
- from子句:指定查询操作的数据源和范围变量
- where子句:筛选元素的逻辑条件,返回值必须是一个bool类型
- select子句:指定查询结果的类型和表现形式
- orderby子句:对查询结果进行排序(升序或降序)
- group… by…子句:对查询结果进行分组
- into子句:提供一个临时标识符,该表示可以充当对join、group、select子句结果的引用
- join子句:连接多个查询操作的数据源
- let子句:引入用于存储查询表达式中的子表达式结果的范围变量
from子句
-
LINQ查询表达式必须包含from子句,并且必须以from子句开始
-
from子句指定的数据源类型必须为IEnumerable、IEnumerable< T>或者两者的派生类型(例如:数组、List< T>、ArrayList等)
//nums:数据源 //item:范围变量:一般表示数据源中的每一个元素 var list = from item in nums where item % 2 != 0 orderby item * 2 descending select item * 2;
-
数据源:
-
如果数据源是泛型类型,则编译器可以自动推断出范围变量的类型
-
如果数据源是非泛型类型,如ArrayList,则必须显示的指定范围变量的数据类型
ArrayList nums = new ArrayList(); nums.AddRange(new int[] { 1,3,6,9,2,8,7}); var list = from int item in nums where item % 2 != 0 orderby item * 2 descending select item * 2; foreach (var item in list) { Console.WriteLine(item); }
-
from复合子查询
如果数据源本事是一个序列的元素还包含子数据源,如果查询子数据源中的元素,则需要复合from子句
public class Student
{
public int StuId { get; set; }
public string StuName { get; set; }
public int StuAge { get; set; }
public List<int> StuScore { get; set; }
}
Student stu1 = new Student() { StuId = 10000, StuAge = 18, StuName = "张三", StuScore = new List<int>() { 89, 68, 76 } };
Student stu2 = new Student() { StuId = 10001, StuAge = 20, StuName = "李四", StuScore = new List<int>() { 78, 88, 86 } };
Student stu3 = new Student() { StuId = 10002, StuAge = 21, StuName = "王五", StuScore = new List<int>() { 90, 75, 82 } };
List<Student> stulist = new List<Student>() { stu1, stu2, stu3 };
//查询成绩中有一门成绩高于89分的所有学员
var res = from item in stulist
from score in item.StuScore
where score > 89
select item;
foreach (var item in res)
{
Console.WriteLine("成绩中包含89分以上的学员有:{0}",item.StuName);
}
from多个子句查询
如果LINQ查询表达式包含两个或两个以上的独立数据源时,可以使用多个from子句查询所有数据源中的数据
var res = from item1 in stulist1
where item1.StuAge > 20
from item2 in stulist2
where item2.StuAge > 20
select new { item1, item2 };
foreach (var item in res)
{
Console.WriteLine("年龄大于20的学员有:{0}",item.item1.StuName);
Console.WriteLine("年龄大于20的学员有:{0}", item.item2.StuName);
}
其他子句
where子句
- 用于指定筛选元素的逻辑条件
- 一个查询表达式可以不包含where子句
- 如果查询表达式包含where子句,则where子句不能放在最后一个子句
select子句
- 用于指定查询结果的类型和表现形式
- LINQ查询表达式或者select子句结束或者以group子句结束
group子句
- 用于对查询结果进行分组
- 返回元素类型为Igrouping< Tkey,TElement >对象序列
orderby子句
- 用于对查询结果排序,默认“升序”
- 在排序字段后面加上descending可以实现降序