Linq入门篇

Linq入门篇

 

一、什么是Linq

Linq是语言集成查询(Language Integrated Query)的简称,是visual Studio 2008和.NET Framework 3.5版本中一项突破性的创新,它在对象领域和数据领域之间架起了一座桥梁。

Linq支持各种数据源:

1、ADO.NET DataSet

2、XML文档

3、SQL Server数据库

4、支持IEnumerable或泛型IEnumerable(T)接口的任意对象集合

5、更多。。。

二、Linq的优点

  • 传统的SQL查询
1 select FirstName,LastName,* from Customers 2 where city = 'Shanghai' 3 order by district

简单的字符串表示,没有编译时类型检查,没有IDE的智能感知支持。

以上例子只是针对SQL,针对不同的数据源,例如XML文档、各种WEB服务等我们还要学习不同的查询方法。

  • Linq查询示例

完全类型检查和IDE智能感知支持。

三、Linq查询的步骤

所有的Linq查询操作都由以下三个不同的操作组成:

  • 获得数据源
  • 创建查询
  • 执行查询

数据源

  • 想要使用Linq进行查询,数据源必须支持IEnumerable或IEnumerable(T)泛型接口或派生接口(如泛型的IQueryable(T)接口)。

查询

  • 查询指定要从数据源中检索的信息
  • 查询还可以指定在返回这些信息之前如何对其进行排序、分组和结构化
  • 查询存储在查询变量中,并用查询表达式进行初始化。为使编写查询的工作更加容易,C#引入了新的查询语法

查询执行

  • 查询变量本身只是存储查询命令。实际的查询执行会延迟在foreach语句中循环访问变量时发生。此概念称为“延迟执行”。
  • 强制立即执行,可以通过以下两个方法,使得Linq立即执行查询

      执行聚合函数(Count、Max、Average、First)。

      调用ToList(<T>Source)或ToArray(<T>Source)方法缓存结果。

四、查询基本操作

1、from子句

      用于获取数据源

1 var queryAllCustomers= 2 from cust in Customers 3 select cust;
  • 查询表达式必须以from子句开头
  • 例子中cust是范围变量,范围变量类似于foreach循环中的迭代变量,但在查询表达式中,实际上不发生迭代。执行查询时,范围变量将用作对Customers中的每个后续元素的引用。因为编译器可以推断cust的类型,所以不必显示指定此类型。
  • Customers是数据源,实现了IEnumerable或IEnumerable(T)或其派生接口的。

2、复合from子句

     在某些情况下,原序列中的每个元素本身可能是序列,也可能包含序列。用于访问某个数据源中的内部集合。

复制代码
1 //数据源 2 IList<Student> students = new List<Student> 3 { 4 new Student{ Name="Kevin", Score=new List<int>{89,93,88,78}}, 5 new Student{ Name="Jackie",Score=new List<int>{92,87,83,91}}, 6 new Student{ Name="Helen",Score=new List<int>{53,76,72,62}} 7 }; 8 9 //使用复合from子句查询命令 10 var getStudent = 11 from student in students 12 from score in student.Score 13 where score > 90 14 select new { Name=student.Name,Score=score};
复制代码

需求:查询出成绩有90分以上的学生,得到他们的名字和成绩

分析:从代码中,我们可以看到学生对象中有个Score属性,Score属性本身就是List集合,这时候我们就要用到复合from子句进行查询了。首先遍历学生对象集合中的每个学生对象,然后在用另一个from子句,对每个学生对象中的Score属性进行遍历,筛选出含有90分以上的学生信息进行返回。

3、使用let子句扩展范围变量

    用于创建查询自身的范围变量

复制代码
1 string[] strings ={ 2 "I am a new Student.", 3 "You are a talent" 4 }; 5 6 var query = from sentences in strings 7 let words = sentences.Split(' ') 8 from word in words 9 let w = word.ToLower() 10 where w[0] == 'a' || w[0] == 'e' || w[0] == 'i' || w[0] == 'o' || w[0] == 'u' 11 select word; 12 13 foreach (var word in query) 14 { 15 Console.Write(word + ","); 16 }
复制代码

需求:将字符串数组中的两句英文语句中所有的元音字母打头的单词输出到控制台

分析:首先遍历字符串数组中的每个字符串,用let子句创建查询自身的范围变量words,并调用Split(' ')方法,将每个字符串中以空格分割为单词存入words变量中,然后再次使用let子句创建查询自身的范围变量word,并调用ToLower()方法,将每个单词都变为小写,最后筛选出首字母为元音的单词进行返回。

4、where子句

     将一个布尔条件("谓词")应用于每个源元素(由范围变量引用),并返回满足指定条件的元素。

复制代码
1 //数据源 2 int[] arr = { 0, 3, 2, 1, 9, 6, 8, 7, 4, 5 }; 3 4 //使用Where子句查询的查询语句 5 var query = from a in arr 6 where a < 5 && a % 2 == 0 7 select a; 8 9 //执行查询 10 foreach (var a in query) 11 { 12 Console.WriteLine(a); 13 }
复制代码

需求:将数组中小于5的偶数查询出来输出到控制台

分析:首先遍历数组中的每个元素,然后用where语句筛选出小于5,并且对2去模是0的数查询出来返回。

     where子句不仅能使用表达式来进行筛选,还可以使用方法进行筛选。

1 public static bool IsEven(int a) 2 { 3 return a % 2 == 0 ? true : false; 4 }
复制代码
1 //数据源 2 int[] arr = { 0, 3, 2, 1, 9, 6, 8, 7, 4, 5 }; 3 4 //where子句也可以接受一个方法 5 var query = from a in arr 6 where IsEven(a) 7 select a; 8 9 foreach (var a in query) 10 { 11 Console.Write(a + ","); 12 }
复制代码

这就是一个在where语句中使用方法进行筛选的例子,输出的结果和上例完全一样。

使用where子句还要注意以下几点

  • 一个查询表达式可以包含多个where子句
  • where子句是一种筛选机制。除了不能是第一个或最后一个子句外,它几乎可以放在查询表达式中的任何位置。where子句可以出现在group子句的前面或后面,具体情况取决于是必须在对源元素进行分组之前还是分组之后来筛选源元素。
  • 如果指定的谓词对于数据源中的元素无效,则会发生编译时错误。这是Linq提供的强类型检查的一个优点。
  • 编译时,where关键字会被转换为对where标准查询运算符方法的调用。

5、orderby子句

  • 对查询出来的结果集进行升序或降序排列。
  • 可以指定多个键,以便执行一个或多个次要排序操作。
  • 默认排序顺序为升序。
  • 编译时,orderby子句将被转换为对OrderBy方法的调用。orderby子句中的多个键转换为ThenBy方法调用。
1 var query = from a in arr 2 where IsEven(a) 3 orderby a ascending 4 select a;

接着上个例子的演示,本例是一个升序的排序。

1 var query = from a in arr 2 where IsEven(a) 3 orderby a descending 4 select a;

本例是一个降序的排序。

6、group子句

  • group子句返回一个IGrouping(T Key,T element)对象序列
  • 编译时,group子句被转换成对GroupBy方法的调用
复制代码
1 //数据源 2 string[] fruits = { "apple", "banana", "peach", "orange", "melon", "lemon" }; 3 4 //分组查询的查询语句 5 var query = from f in fruits 6 group f by f[0]; 7 8 //执行查询 9 foreach (var letters in query) 10 { 11 Console.WriteLine("words that start with letter:" + letters.Key); 12 foreach (var word in letters) 13 { 14 Console.WriteLine(word); 15 } 16 }
复制代码

需求:根据首字母分组,并打印到控制台

分析:首先遍历字符串数组中的每个字符串,然后根据每个字符串的首字母进行分组,返回结果

1 var query = from f in fruits 2 group f by f[0] into g 3 where g.Key == 'p' || g.Key == 'b' 4 select g;

如果您想要对每个组执行附加查询操作,则可以使用into上下文关键字指定一个临时标识符。使用into时,必须继续编写该查询,并最终用一个select语句或另一个group子句结束该查询。

7、join子句

  • 使用join子句可以将来自不同源序列并且在对象模型中没有直接关系的元素相关联。
  • 唯一的要求是每个源中的元素需要共享某个可以进行比较以判断是否相等的值。
  • join子句使用特殊的equals关键字比较指定的键是否相等。

1)内部连接

1 var innerJoinQuery = 2 from category in categories 3 join prod in products on category.ID equals prod.CategoryID 4 select new { ProductName = prod.Name, Category = category.Name };

2)分组连接

1 var innerGroupJoinQuery = 2 from category in categories 3 join prod in products on category.ID equals prod.CategoryID 4 into prodGroup 5 select new { CategoryName = category.Name, Products = prodGroup };

3)左外部连接

复制代码
1 var leftOuterJoinQuery = 2 from category in categories 3 join prod in products on category.ID equals prod.CategoryID 4 into prodGroup 5 from item in prodGroup.DefaultIfEmpty(new Product{Name = 6 string.Empty, CategoryID = 0}) 7 select new { CatName = category.Name, ProdName = item.Name };
复制代码

在左外连接中,将返回左侧源序列中的所有元素,即使它们在右侧序列中没有匹配的元素也是如此。

若要在Linq中执行左外连接,请将DefaultIfEmpty方法与分组连接结合起来,以指定要在某个元素不具有匹配元素时产生的默认右侧元素,可以使用null作为任何引用类型的默认值。也可以指定用户定义的默认类型。

8、equals关键字

  • join子句执行同等连接。换句话说,只能基于两个键之间的相等关系进行匹配。
  • 为了表明所有连接都是同等连接,join子句使用equals关键字而不是==运算符

9、select子句(选择、投影)

select子句可以指定将在执行查询时产生的值的类型。该子句的结果将基于前面所有子句的计算结果以及select子句本身中的所有表达式。

查询表达式必须以select子句或group子句结束

在最简单的情况下,select子句仅指定范围变量。这会使返回的序列包含于数据源具有相同类型的元素。

原文地址:https://www.cnblogs.com/qwg123/p/4550802.html