前言
距离上一次更新,已经过去了一周,说起来实在是有些惭愧。一方面,近日的工作的重心在于前端api这块,没有时间精力再去补充C#的相关知识,另一方面,感觉学习的劲头确实不如从前了,近日提笔,也算是一种反思惊醒。今日整理的Linq知识,也是我一直想整理的,尤其在遇到EFcore进行联合查询的时候就遇到了linq,于是便有了下文。
本文整理转载于博客园:https://www.cnblogs.com/dotnet261010/p/8278793.html。
知乎:https://zhuanlan.zhihu.com/p/29049086
什么是linq以及为什么使用linq?
长期以来,开发社区形成以下的格局:
1、面向对象与数据访问两个领域长期分裂,各自为政。
2、编程语言中的数据类型与数据库中的数据类型形成两套不同的体系,例如:
C#中字符串用string数据类型表示。
SQL中字符串用NVarchar/Varchar/Char数据类型表示。
3、SQL编码体验落后
没有智能感知效果。
没有严格意义上的强类型和类型检查。
4、SQL和XML都有各自的查询语言,而对象没有自己的查询语言。
上面描述的问题,都可以使用LINQ解决,那么究竟什么是LINQ呢?
LINQ(Language Integrated Query)即语言集成查询。
LINQ是一组语言特性和API,使得你可以使用统一的方式编写各种查询。用于保存和检索来自不同数据源的数据,从而消除了编程语言和数据库之间的不匹配,以及为不同类型的数据源提供单个查询接口.
LINQ总是使用对象,因此你可以使用相同的查询语法来查询和转换XML、对象集合、SQL数据库、ADO.NET数据集以及任何其他可用的LINQ提供程序格式的数据。
LINQ主要包含以下三部分:
1、LINQ to Objects 主要负责对象的查询。
2、LINQ to XML 主要负责XML的查询。
3、LINQ to ADO.NET 主要负责数据库的查询。
LINQ to SQL
LINQ to DataSet
LINQ to Entities
使用LINQ的优势
1、熟悉的语言:开发人员不必为每种类型的数据源或数据格式学习新的语言。
2、更少的编码:相比较传统的方式,LINQ减少了要编写的代码量。
3、可读性强:LINQ增加了代码的可读性,因此其他开发人员可以很轻松地理解和维护。
4、标准化的查询方式:可以使用相同的LINQ语法查询多个数据源。
5、类型检查:程序会在编译的时候提供类型检查。
6、智能感知提示:LINQ为通用集合提供智能感知提示。
7、整形数据:LINQ可以检索不同形状的数据。
Linq的操作语法
总的来说,LINQ查询时有两种语法可供选择:查询表达式语法(Query Expression)和方法语法(Fluent Syntax)。
一、查询表达式语法
查询表达式语法是一种更接近SQL语法的查询方式。
LINQ查询表达式语法如下:
1 from<range variable> in <IEnumerable<T> or IQueryable<T> Collection>
2 <Standard Query Operators> <lambda expression>
3 <select or groupBy operator> <result formation>
约束 | LINQ查询表达式必须以from子句开头,以select或group子句介绍 |
关键字 | 功能 |
from....in... | 指定要查询的数据源以及范围变量,多个from子句则表示从多个数据源查找数据。注意:C#编译器会把“复合from子句”的查询表达式转换为SelectMany()扩展方法。 |
join…in…on…equals… | 指定多个数据源的关联方式 |
let | 引入用于存储查询表达式中子表达式结果的范围变量。通常能达到层次感会更好,使代码更易于阅读。 |
orderby、descending | 指定元素的排序字段和排序方式。当有多个排序字段时,由字段顺序确定主次关系,可指定升序和降序两种排序方式 |
where | 指定元素的筛选条件。多个where子句则表示了并列条件,必须全部都满足才能入选。每个where子句可以使用谓词&&、||连接多个条件表达式。 |
group | 指定元素的分组字段。 |
select | 指定查询要返回的目标数据,可以指定任何类型,甚至是匿名类型。(目前通常被指定为匿名类型) |
into | 提供一个临时的标识符。该标识可以引用join、group和select子句的结果。 1) 直接出现在join子句之后的into关键字会被翻译为GroupJoin。(into之前的查询变量可以继续使用) 2) select或group子句之后的into它会重新开始一个查询,让我们可以继续引入where, orderby和select子句,它是对分步构建查询表达式的一种简写方式。(into之前的查询变量都不可再使用) |
查询语法从一个From子句开始,然后是一个Range变量。 From子句的结构类似于“From rangeVariableName in IEnumerablecollection”。 在英语中,这意味着,从集合中的每个对象。 它类似于foreach循环:foreach(student in studentList)。
在From子句之后,您可以使用不同的标准查询运算符来过滤,分组,连接集合的元素。 LINQ中有大约50个标准查询运算符。标准查询运算符后面通常跟一个条件,这个条件通常使用lambda表达式来表示。
LINQ查询语法总是以Select或Group子句结束。 Select子句用于对数据进行整形。 您可以选择整个对象,因为它是或只有它的一些属性。 在上面的例子中,我们选择了每个结果字符串元素。
例如:我们要从数组中查询出偶数,查询表达式示例代码如下:
var result = from p in ints where p % 2 == 0 select p;
查询表达式语法要点总结:
1、查询表达式语法与SQL(结构查询语言)语法相同。
2、查询语法必须以from子句开头,可以以Select或GroupBy子句结束 。
3、使用各种其他操作,如过滤,连接,分组,排序运算符以构造所需的结果。
4、隐式类型变量 - var可以用于保存LINQ查询的结果。
Linq的Lambda表达式
通常情况下,Lambda表达式与LINQ查询一起使用。枚举静态类包括接受Func <TSource,bool>的IEnumerable <T>的Where扩展方法。IEnumerable <Int>集合的Where()扩展方法需要传递Func <Student,bool>,如下所示:
现在,您可以将分配给Func委托的lambda表达式传递给方法语法中的Where()扩展方法,如下所示:
Func<int, bool> isContains = p =>p.Equals (4);
2 int[] ints = { 5, 2, 0, 66, 4, 32, 7, 1 };
3 var result = ints.Where(isContains).ToList();
Linq查询表达式总结
●join子句:连接多个用于查询操作的数据源。
例如:
int[]arr =new int[]{0,1,2,3,4,5,6,7,8,9};
var query1=from n in arr
select n;
var query2=from n in arr
where n >6
select n;
2.From字句
from子句用来指定查询表达式的数据源和范围变量。它是查询表达式的必有部分,并且它出现在最开始。数据源不但可以包括查询本身的数据源,而且还可以包括子查询的数据源。范围变量用来表示数据源序列中的每一个元素。
注意:from子句指定的数据源的类型必须为IEnumerable,IEnumerable<T>或一种派生类型。
在查询表达式中,from子句至少有一个,只有一个from的查询表达式称为单个from子句表达式。上面的query1和query2都是这类。
另一种类型叫做包含多个from子句的查询表达式,例子如下:
int[] arr1= new int[] {0,1,2,3,4,5,6,7,8,9};
int[] arr2=new int[] {0,1,2,3,4,5,6,7,8,9};
var query =from a in arr1
from b in arr2
select a +b;
查询同一个数据源的例子:
public class UserInfo
{
Public string Name;
Public List<string> AliasName;
}
List<UserInfo> list =...
var query =from u in list
from name in u.AliasName
select u.Name+name;
3.Select 子句
select子句用来指定将在执行查询时产生的值的类型。查询表达式必须以select子句或group子句结束。
如果我们继续使用上面的类UserInfo:
每个对象的ID都是一个UserInfo型的对象,Name是这个对象的Name
4.Where子句
where子句用来指导将在查询表达式中返回数据源中的哪些元素。它将一个布尔条件应用于数据源中的每个元素,并返回指定条件的元素。查询表达式可以包含一个或多个where子句。
int[] arr =new int[] {0,1,2,3,4,5,6,7,8,9};
var query =from n in arr
where n <3 && n >0 //使用&&或者||分隔多个where 条件
select n;
甚至可以在where子句中使用返回布尔型的方法:
5.Let子句
let子句用来创建一个新的范围变量,它用于存储子表达式的结果。let子句使用编程者提供的表达式的结果初始化该变量。一旦初始化了该范围变量的值,它就不能用于存储其他的值。
例如:
就可以找到所有由元音字幕开头的单词了
6. orderby子句
orderby子句可使返回的查询结果按升序或者降序排序。升序由关键字ascending指定,而降序由关键字descending指定。
注意:orderby子句默认排序方式为升序。
orderby子句可以包含一个或多个排序表达式,各个排序表达式使用逗号(,)分隔。
例如:
int[] arr =new int[]{0,1,2,3,4,5,6,7,8,9};
var query =from n in arr
where n>1 && n<6
orderby n descending
select n ;
7.groupby子句
group子句用来将查询结果分组,并返回一对象序列。这些对象包含零个或更多个与改组的key值匹配的项,还可以使用group子句结束查询表达式。
注意:每一个分组都不是单个元素,而是一个序列(也属于集合)。
如果我们执行一下,会发现结果其实是分成两组的:
我们可以调用如下方式去查看每个组的key和里面元素的数目:
结果如下:
IGrouping<TKey, TElement> 的键表示 IGrouping<TKey, TElement> 中的每个值所共有的属性。
8.into子句
into子句可以用来创建一个临时标识符,将group、join或select子句的结果存储到这个标识符中。
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var query = from n in arr
where n > 1 && n < 7
group n by n % 2 into g
from sn in g
select sn;
Console.WriteLine(query.Count()); //结果是5
//结果是2 4 6 3 5
foreach (int i in query) {
Console.WriteLine(i);
}
9.join子句
join子句用来连接两个数据源,即设置两个数据源之间的关系。join子句支持以下3种常见联接方式。
9.1 内部连接
join子句的内部联接要求两个数据源都必须存在相同的值,即两个数据源都必须存在满足联接关系的元素,类似于SQL的inner join。
示例1
join b in arrb on a equals b: 将arra和arrb数组进行联接,同时满足a和b相等的条件。其中,b元素是arrb数组中的元素。
9.2 分组连接
join子句的分组联接包含into子句的join子句的链接。它将左数据源与右数据源的元素一次匹配。左数据源的所有元素都出现在查询结果中。若在右数据源中找到匹配项,则使用匹配的数据,否则用空表示。
9.3 左外部连接
join子句的左外部链接将返回左侧数据源序列中的所有元素,就算它们在右侧序列中没有匹配的元素也是。