[.net 物件導向程式設計基礎] (20) LINQ使用

yubinfeng發表於2015-06-13

[.net 物件導向程式設計基礎] (20)  LINQ使用

通過上節LINQ的基礎知識的學習,我們可以開始使用LINQ來進行記憶體資料的查詢了,我們上節說了LINQ的定義為:Language Integrated Query(語言整合查詢)的簡稱,它是整合在.NET程式語言中的一種特性.

 1.LINQ的構架

 

 從這幅圖中,我們可以知道LINQ包括五個部分:LINQ to Objects、LINQ to XML、LINQ to SQL、LINQ to DataSet、LINQ to Entities。 

 

程式集

名稱空間

描述

LINQ to Objects

System.Core.dll

System.Linq

提供對記憶體中集合操作的支援

LINQ to XML

System.Xml.Linq.dll

System.Xml.Linq

提供對XML資料來源的操作的支援

LINQ to SQL

System.Data.Linq.dll

System.Data.Linq

提供對Sql Server資料來源操作的支援。(微軟已宣佈不再更新,推薦使用LINQ to Entities)

LINQ to DataSet

System.Data.DataSetExtensions.dll

System.Data

提供對離線資料操作的支援。

LINQ to Entities

System.Core.dll 和System.Data.Entity.dll

System.Linq 和System.Data.Objects

LINQ to Entities 是 Entity Framework 的一部分並且取代LINQ to SQL 作為在資料庫上使用 LINQ 的標準機制。(Entity Framework 是由微軟釋出的開源物件-關係對映(ORM)框架,支援多種資料庫。)

 目前,還可以下載其他第三方提供程式,例如LINQ to JSON、LINQ to MySQL、LINQ to Amazon、LINQ to Flickr和LINQ to SharePoint。無論使用什麼資料來源,都可以通過LINQ使用相同的API進行操作。

 1.LINQ操作語法 

LINQ查詢時有兩種語法可供選擇:查詢表示式(Query Expression)和方法語法(Fluent Syntax)

 .NET公共語言執行庫(CLR)並不具有查詢表示式的概念。所以,編譯器會在程式編譯時把查詢表示式轉換為方法語法,即對擴充套件方法的呼叫。所以使用方法語法會讓我們更加接近和了解LINQ的實現和本質,並且一些查詢只能表示為方法呼叫。但另一方面,查詢表示式通常會比較簡單和易讀。不管怎樣,這兩種語法是互相補充和相容的,我們可以在一個查詢中混合使用查詢表示式和方法語法。 

 .net的設計者在類庫中定義了一系列的擴充套件方法來方便使用者操作集合物件,這些擴充套件方法構成了LINQ的查詢操作符

 以下擴充套件方法存在對應的查詢表示式關鍵字:Where、Select、SelectMany、OrderBy、ThenBy、OrderByDescending、ThenByDescending、GroupBy、Join、GroupJoin。

 LINQ查詢表示式 

約束

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 [type] id in source      

[join [type] id in source on expr equals expr [into subGroup]]      

[from [type] id in source | let id = expr | where condition]      

[orderby ordering,ordering,ordering...]      

select expr | group expr by key      

[into id query]

<1>第一行: 

type是可選的,id是集合中的一項,source是一個集合,如果集合中的型別與type指定的型別不同則導致強制轉化

 <2>第二行:  

一個查詢表示式中可以有0個或多個join子句,
這裡的source可以不等於第一句中的source

 expr可以是一個表示式 

[into subGroup] subGroup是一箇中間變數,它繼承自IGrouping,代表一個分組,也就是說“一對多”裡的“多”

 可以通過這個變數得到這一組包含的物件個數,以及這一組物件的鍵

<3>第三行:  

一個查詢表示式中可以有1個或多個from子句 

一個查詢表示式中可以有0個或多個let子句,let子句可以建立一個臨時變數 

一個查詢表示式中可以有0個或多個where子句,where子句可以指定查詢條件 

<4>第四行: 

一個查詢表示式可以有0個或多個排序方式 

每個排序方式以逗號分割 

<5>第五行: 

一個查詢表示式必須以select或者group by結束 

select後跟要檢索的內容 

group by 是對檢索的內容進行分組 

<6>第六行: 

最後一個into子句起到的作用是將前面語句的結果作為後面語句操作的資料來源

 3. LINQ to Objects 

LINQ to ObjectsLINQ的精華部分。 

LINQ to Objects 提供對記憶體中集合操作的支援,由程式集System.Core.dllSystem.Linq名稱空間下的Enumerable靜態類提供。 

 

這些擴充套件方法都是針對IEnumerable的物件進行擴充套件的也就是說,只要實現了IEnumerable介面,就可以使用這些擴充套件方法

4.示例

 我們說對於LINQ查詢語法,有兩種可供選擇,一種是擴充套件方法(又叫方法語法(Fluent Syntax),另一種是查詢表示式(Query Expression。他們是等價的,只是寫法上不同。

在下面的示例中,我們都會用兩種語法來完成。

編號

Id

姓名

Name

年齡

Age

門派

Menpai

武學

Kungfu

武學級別

Level

1

黃蓉

18

丐幫

打狗棒法

9

2

洪七公

70

丐幫

打狗棒法

10

3

郭靖

22

丐幫

降龍十八掌

10

4

任我行

50

明教

葵花寶典

1

5

東方不敗

35

明教

葵花寶典

10

6

林平之

23

華山

葵花寶典

7

7

嶽不群

50

華山

葵花寶典

8

 

編號

KongfuId

武學名稱

KongfuName

殺傷力

Lethality

1

打狗棒法

90

2

降龍十八掌

95

3

葵花寶典

100

 如上物件“武林高手”(MartialArtsMaster)和“武學”(Kongfu 

 1 /// <summary>
 2 /// 類:武林高手
 3 /// MartialArtsMaster
 4 /// </summary>
 5 class  MartialArtsMaster
 6 {
 7     /// <summary>
 8     /// 編號
 9     /// </summary>
10     public int Id{get;set;}
11     /// <summary>
12     /// 姓名
13     /// </summary>
14     public string Name { get; set; }
15     /// <summary>
16     /// 年齡
17     /// </summary>
18     public int Age { get; set; }
19     /// <summary>
20     /// 門派
21     /// </summary>
22     public string Menpai { get; set; }
23     /// <summary>
24     /// 武學
25     /// </summary>
26     public string Kungfu { get; set; }
27     /// <summary>
28     /// 級別
29     /// </summary>
30     public int Level { get; set; }
31 }
32 
33 /// <summary>
34 /// 類:武學
35 /// Kongfu
36 /// </summary>
37 class Kongfu
38 {
39     /// <summary>
40     /// 武學編號
41     /// </summary>
42     public int KongfuId { get; set; }
43 
44     /// <summary>
45     /// 武學名稱
46     /// </summary>
47     public string KongfuName { get; set; }
48 
49     /// <summary>
50     /// 殺傷力
51     /// </summary>
52     public int Lethality { get; set; }
53 }

 

//初始化武林高手
var master = new List<MartialArtsMaster>(){ 
    new MartialArtsMaster(){ Id = 1, Name = "黃蓉",    Age = 18, Menpai = "丐幫", Kungfu = "打狗棒法",  Level = 9  },
    new MartialArtsMaster(){ Id = 2, Name = "洪七公",  Age = 70, Menpai = "丐幫", Kungfu = "打狗棒法",  Level = 10 },
    new MartialArtsMaster(){ Id = 3, Name = "郭靖",    Age = 22, Menpai = "丐幫", Kungfu = "降龍十八掌",Level = 10 },
    new MartialArtsMaster(){ Id = 4, Name = "任我行",  Age = 50, Menpai = "明教", Kungfu = "葵花寶典",  Level = 1  },
    new MartialArtsMaster(){ Id = 5, Name = "東方不敗",Age = 35, Menpai = "明教", Kungfu = "葵花寶典",  Level = 10 },
    new MartialArtsMaster(){ Id = 6, Name = "林平之",  Age = 23, Menpai = "華山", Kungfu = "葵花寶典",  Level = 7  },
    new MartialArtsMaster(){ Id = 7, Name = "嶽不群",  Age = 50, Menpai = "華山", Kungfu = "葵花寶典",  Level = 8  }         
};
//初始化武學
var kongfu = new List<Kongfu>(){
    new Kongfu(){KongfuId=1, KongfuName="打狗棒法", Lethality=90},
    new Kongfu(){KongfuId=2, KongfuName="降龍十八掌", Lethality=95},
    new Kongfu(){KongfuId=3, KongfuName="葵花寶典", Lethality=100}
}

4.1過濾操作符

根據條件返回匹配元素的集合IEnumerable<T>。

1)  Where:根據返回bool值的Func委託引數過濾元素。

業務說明:查詢獲得車手冠軍次數大於15次且是Austria國家的一級方程式賽手

2)  OfType<TResult>:接收一個非泛型的IEnumerable集合,根據OfType泛型型別引數過濾元素,只返回TResult型別的元素。

業務說明:過濾object陣列中的元素,返回字串型別的陣列。

3)  Distinct:刪除序列中重複的元素。 

示例一:查詢 丐幫 中 修行"級別"高於 "8的大俠 

 1 //示例一:查詢 丐幫 中 修行"級別"高於 "8級" 的大俠
 2 
 3 //表示式 寫法
 4 var GaiBangMaster = from   m in master
 5         where  m.Level > 8 && m.Menpai == "丐幫"
 6         select m;
 7 
 8 //擴充套件方法 寫法
 9 var GaiBangMasterMethod = master.Where(m => m.Level > 8 && m.Menpai == "丐幫");
10 
11 //輸出結果
12 string GaiBangMasterResult="查詢\"丐幫\"中\"功力\"高於80的大俠(表示式寫法):\n";
13 GaiBangMasterResult +="編號(Id) 姓名(Name) 年齡(Age) 門派(Mengpai) 武學(Kungfu) 級別(Level)\n";
14 
15 foreach(var m in GaiBangMaster)
16   GaiBangMasterResult += m.Id + "  " + m.Name+"  "+m.Age+"  "+m.Menpai+"  "+m.Kungfu+"  "+m.Level+"  " + "\n";
17 Console.WriteLine(GaiBangMasterResult);
18 
19 string GaiBangMasterMethodResult = "查詢\"丐幫\"中\"功力\"高於80的大俠(擴充套件方法 寫法):\n";
20 GaiBangMasterMethodResult += "編號(Id) 姓名(Name) 年齡(Age) 門派(Mengpai) 武學(Kungfu) 級別(Level)\n";
21 
22 foreach (var m in GaiBangMasterMethod)
23   GaiBangMasterMethodResult += m.Id + "  " + m.Name + "  " + m.Age + "  " + m.Menpai + "  " + m.Kungfu + "  " + m.Level + "  " + "\n";
24 Console.WriteLine(GaiBangMasterMethodResult);
25 
26 Console.ReadKey();

 輸出結果如下:

4.2.  投影操作符

1)  Select 將序列的每個元素經過lambda表示式處理後投影到一個新型別元素上。(與SelectMany不同在於,若單個元素投影到IEnumerable<TResult>,Select不會對多個IEnumerable<TResult>進行合併)

2) SelectMany

a)   c#編譯器會把“複合from子句”的查詢表示式轉換為SelectMany()擴充套件方法。

b) 將序列的每個元素經過lambda表示式處理後投影到一個 IEnumerable<TResult>,再將多個IEnumerable<TResult>序列合併為一個返回序列IEnumerable<TResult>。

 c)  將序列的每個元素經過lambda表示式處理後投影到一個 IEnumerable<TCollection>,再將多個IEnumerable<TCollection>序列合併為一個返回序列IEnumerable<TCollection>,並對其中每個元素呼叫結果選擇器函式。

 示例二:過濾所學”武功” “傷殺力” 大於90 的大俠 

 1 //示例二:過濾所學”武功” “傷殺力” 大於90 的大俠
 2 //表示式 寫法
 3 var masterKongfu = from m in master
 4                    from k in kongfu
 5                    where( k.Lethality > 90 && m.Kungfu==k.KongfuName)
 6                    orderby m.Level
 7                    select m.Id + "  " + m.Name + "  " + m.Age + "  " + m.Menpai + "  " + m.Kungfu + "  " + m.Level + "  ";
 8 //擴充套件方法 寫法
 9 var masterKongfuMethod = master
10                         .SelectMany(
11                            k => kongfu,
12                            (m, k) => new { mt = m, kf = k }
13                         )
14                         .Where(x =>x.kf.Lethality> 90 && x.mt.Kungfu==x.kf.KongfuName )                       
15                         .OrderBy(m => m.mt.Level)
16                         .Select(m => m.mt.Id + "  " + m.mt.Name + "  " + m.mt.Age + "  " + m.mt.Menpai + "  " + m.mt.Kungfu + "  " + m.mt.Level + "  ");
17 //輸出結果
18 string masterKongfuResult = "過濾所學”武功” “傷殺力” 大於90 的大俠(表示式寫法):\n";
19 masterKongfuResult += "編號(Id) 姓名(Name) 年齡(Age) 門派(Mengpai) 武學(Kungfu) 級別(Level)\n";
20 foreach (var m in masterKongfu)
21     masterKongfuResult += m.ToString()+"  " + "\n";
22 Console.WriteLine(masterKongfuResult);
23 
24 string masterKongfuMethodResult = "過濾所學”武功” “傷殺力” 大於90 的大俠(擴充套件方法 寫法):\n";
25 masterKongfuMethodResult += "編號(Id) 姓名(Name) 年齡(Age) 門派(Mengpai) 武學(Kungfu) 級別(Level)\n";
26 foreach (var m in masterKongfuMethod)
27     masterKongfuMethodResult += m.ToString() + "  " + "\n";
28 Console.WriteLine(masterKongfuMethodResult);
29 
30 Console.ReadKey();

 輸出結果如下:

4.3排序操作符

1)      OrderBy<TSource,TKey>,OrderByDescending<TSource,TKey>:根據指定鍵按升序或降序對集合進行第一次排序,輸出IOrderedEnumerable<TSource>。

2)        ThenBy<TSource,TKey>,ThenByDescending<TSource,TKey>:只會對那些在前一次排序中擁有相同鍵值的elements重新根據指定鍵按升序或降序排序。輸入IOrderedEnumerable <TSource>。

   3)      Reverse<TSource>:反轉集合中所有元素的順序。 

示例三:“華山論劍”之武林排行榜 (注:武功高低=所學武學殺傷力*個人武學級別) 排序規則,先按武功高低(由高到低),再按年齡(從小到大),再按名字首字母(正序)

 1 //示例三:“華山論劍”之武林排行榜 (注:武功高低=所學武學殺傷力*個人武學級別) 排序規則,先按武功高低(由高到低),再按年齡(從小到大),再按名字首字母(正序)
 2 //表示式寫法
 3 int i=0;
 4 var topMaster= from m in master
 5                     from k in kongfu
 6                 where (k.KongfuName==m.Kungfu)
 7                 orderby m.Level*k.Lethality descending,m.Age,m.Name
 8                 select m.Id + "  " + m.Name + "  " + m.Age + "  " + m.Menpai + "  " + m.Kungfu + "  " + m.Level + "  "+m.Level*k.Lethality +"  "+(++i);
 9 //輸出結果
10 string topMasterResult = "“華山論劍”之武林排行榜 (注:武功高低=所學武學殺傷力*個人武學級別)(表示式 寫法):\n";
11 topMasterResult += "編號  姓名  年齡  門派  武學  級別  武功  排名\n";
12 foreach (var m in topMaster)
13     topMasterResult += m.ToString() + "  " + "\n";
14 Console.WriteLine(topMasterResult);
15 
16 //擴充套件方法寫法
17 i = 0;
18 var topMasterMethod = master
19             .SelectMany(
20                 k => kongfu,
21                 (m, k) => new { mt = m, kf = k }
22             )
23             .Where(x => x.mt.Kungfu == x.kf.KongfuName)
24             .OrderByDescending(m => m.mt.Level*m.kf.Lethality)
25             .ThenBy(m=>m.mt.Age)
26             .ThenBy(m=>m.mt.Name)
27             .Select(m => m.mt.Id + "  " + m.mt.Name + "  " + m.mt.Age + "  " + m.mt.Menpai + "  " + m.mt.Kungfu + "  " + m.mt.Level + "  " + m.mt.Level * m.kf.Lethality + "  " + (++i));
28 //輸出結果
29 string topMasterMethodResult = "“華山論劍”之武林排行榜 (注:武功高低=所學武學殺傷力*個人武學級別)(擴充套件方法 寫法):\n";
30 topMasterMethodResult += "編號  姓名  年齡  門派  武學  級別  武功  排名\n";
31 foreach (var m in topMasterMethod)
32     topMasterMethodResult += m.ToString() + "  " + "\n";
33 Console.WriteLine(topMasterMethodResult);
34 
35 Console.ReadKey();

輸出結果如下:

4.4連線操作符

注意:join…on…關鍵字後的相等使用equals關鍵字。 

 1 //獲取集合MasterTop (武功個人級別大於8的高手)
 2 int ii = 1;
 3 var MasterTop = master
 4  .Where(x => x.Level > 8)
 5  .OrderByDescending(x => x.Level)
 6  .Select(x => new { Id = x.Id, Name = x.Name, MasterKongfu = x.Kungfu, Level = x.Level, Top = (ii++) });
 7                         
 8 
 9 //獲取集合KongfuTop (武功殺傷力大於90的武功)
10 ii=1;
11 var KongfuTop = from k in kongfu
12     where (k.Lethality>90)
13     orderby k.Lethality descending
14     select new{ KongfuId=k.KongfuId,KongfuName=k.KongfuName,Lethality=k.Lethality,KongfuTop=(ii++)};

在此之前,我們先獲取兩個物件MasterTop (武功個人級別大於8的高手)和物件KongfuTop(武功殺傷力大於90的武功)

1) Join:基於匹配鍵對兩個序列的元素進行關聯。

示例三:通過物件MasterTop 和 KongfuTop關聯,返回新的物件 MasterLethalityTop高手殺傷力(包含高手的編號,名字,所學武功,級別,總殺傷力) 

 1 //示例三:通過物件MasterTop 和 KongfuTop關聯,返回新的物件 MasterLethalityTop高手殺傷力(包含高手的編號,名字,所學武功,級別,總殺傷力)
 2 //表示式
 3 var MasterLethalityTop = from m in MasterTop
 4                          join k in KongfuTop on m.MasterKongfu equals k.KongfuName
 5                          orderby m.Level*k.Lethality descending
 6                          select new
 7                          {
 8                              Id = m.Id,Name=m.Name,Kongfu=m.MasterKongfu,Level=m.Level,Kill=m.Level*k.Lethality
 9                          };
10 //擴充套件方法
11 var MasterLethalityTopMothod = MasterTop.Join(KongfuTop,
12                                     m => m.MasterKongfu,
13                                     k => k.KongfuName,
14                                    (m, k) => new
15                                    {
16                                        Id = m.Id,
17                                        Name = m.Name,
18                                        Kongfu = m.MasterKongfu,
19                                        Level = m.Level,
20                                        Kill = m.Level * k.Lethality
21                                    }
22                               )
23                               .OrderByDescending(m => m.Kill);
24 
25 Console.WriteLine("通過物件MasterTop 和 KongfuTop關聯,返回新的物件 MasterLethalityTop高手殺傷力(表示式):\n");
26 Console.WriteLine("編號     名字     所學武功     級別     總殺傷力\n");
27 foreach (var ma in MasterLethalityTop)
28     Console.WriteLine(ma.Id + "  " + ma.Name + "  " + ma.Kongfu + "  " + ma.Level + "  " + ma.Kill + "\n");
29 
30 Console.WriteLine("通過物件MasterTop 和 KongfuTop關聯,返回新的物件 MasterLethalityTop高手殺傷力(擴充套件方法):\n");
31 Console.WriteLine("編號     名字     所學武功     級別     總殺傷力\n");
32 foreach (var ma in MasterLethalityTopMothod)
33     Console.WriteLine(ma.Id + "  " + ma.Name + "  " + ma.Kongfu + "  " + ma.Level + "  " + ma.Kill + "\n");
34 Console.ReadKey();

執行結果如下:

 

2)   GroupJoin:基於鍵相等對兩個序列的元素進行關聯並對結果進行分組。常應用於返回“主鍵物件-外來鍵物件集合”形式的查詢。

注意:直接出現在join子句之後的into關鍵字會被翻譯為GroupJoin,而在select或group子句之後的into表示繼續一個查詢。

示例三: 使用武學關聯masterkongfu,並按武學分組;並按使用者數量排序 

 1 //示例三:過濾操作符GroupJoin 使用武學關聯master和kongfu,並按武學分組;並按使用者數量排序
 2             
 3 //增加幾個武林高手
 4 master.Add(new MartialArtsMaster() { Id = 8, Name = "令狐沖", Age = 23, Menpai = "華山", Kungfu = "獨孤九劍", Level = 10 });
 5 master.Add(new MartialArtsMaster() { Id = 9, Name = "梅超風", Age = 23, Menpai = "桃花島", Kungfu = "九陰真經", Level = 8 });
 6 master.Add(new MartialArtsMaster() { Id =10, Name = "黃藥師", Age = 23, Menpai = "梅花島", Kungfu = "彈指神通", Level = 10 });
 7 master.Add(new MartialArtsMaster() { Id = 11, Name = "風清揚", Age = 23, Menpai = "華山", Kungfu = "獨孤九劍", Level = 10 });
 8 
 9 //增加幾個武學
10 kongfu.Add(new Kongfu() { KongfuId=  4, KongfuName = "獨孤九劍", Lethality = 100 });
11 kongfu.Add(new Kongfu() { KongfuId = 5, KongfuName = "九陰真經", Lethality = 100 });
12 kongfu.Add(new Kongfu() { KongfuId = 6, KongfuName = "彈指神通", Lethality = 100 });
13 
14 Console.WriteLine("過濾操作符GroupJoin 使用武學關聯master和kongfu,並按武學分組 (表示式):\n");
15 Console.WriteLine("武學編號 名字 殺傷力 學會的大俠數\n");
16 var masterItems = from k in kongfu
17                   join m in master on k.KongfuName equals m.Kungfu
18                   into groups
19                   orderby groups.Count() descending
20                   select new
21                   {
22                     KongfuId = k.KongfuId,
23                     KongfuName = k.KongfuName,
24                     Lethality = k.Lethality,
25                     Count=groups.Count()
26                   };            
27 foreach(var ma in masterItems)
28 Console.WriteLine(ma.KongfuId + "  " + ma.KongfuName + "  " + ma.Lethality + "  " + ma.Count + "\n");
29 
30 
31 Console.WriteLine("過濾操作符GroupJoin 使用武學關聯master和kongfu,並按武學分組 (擴充套件方法):\n");
32 Console.WriteLine("武學編號 名字 殺傷力 學會的大俠數\n");
33 var masterItemsMothod = kongfu.GroupJoin(master,
34              k => k.KongfuName, m => m.Kungfu,
35              (k, m) => new { k.KongfuId, k.KongfuName, k.Lethality, Count = m.Count() }
36              )
37              .OrderByDescending(k => k.Count);
38            
39 foreach (var ma in masterItemsMothod)
40     Console.WriteLine(ma.KongfuId+"  "+ma.KongfuName + "  " + ma.Lethality +"  "+ ma.Count + "\n");

 輸出結果如下:

3) join…on…equals…支援多個鍵關聯

可以使用匿名型別來對多個鍵值進行Join,如下所示:

                from x in sequenceX

                join y in sequenceY on new { K1 = x.Prop1, K2 = x.Prop2 }

                equals new { K1 = y.Prop3, K2 = y.Prop4 }

                ...

兩個匿名型別的結構必須完全一致,這樣編譯器會把它們對應到同一個實現型別,從而使連線鍵值彼此相容。

4)  Join與GroupJoin結果集對比 

Join操作符執行一個內連線(inner join), 輸出一個扁平序列

 

 1 //Join和GroupJoin區別
 2 
 3 Console.WriteLine("Join和GroupJoin區別,使用Join (表示式):\n");
 4 Console.WriteLine("武學編號 名字 殺傷力 使用大俠名字\n");
 5 var JoinOrGroupJoin = from k in kongfu
 6                   join m in master on k.KongfuName equals m.Kungfu                  
 7                   orderby k.KongfuId
 8                   select new
 9                   {
10                       KongfuId = k.KongfuId,
11                       KongfuName = k.KongfuName,
12                       Lethality = k.Lethality,
13                      Master= m.Name
14                   };
15 foreach (var ma in JoinOrGroupJoin)
16     Console.WriteLine(ma.KongfuId + "  " + ma.KongfuName + "  " + ma.Lethality + "  " + ma.Master + "\n");
17 
18 
19 Console.WriteLine("Join和GroupJoin區別,使用GroupJoin(擴充套件方法):\n");
20 Console.WriteLine("武學編號 名字 殺傷力 學會的大俠數\n");
21 var JoinOrGroupJoinMethod = kongfu.GroupJoin(master,
22              k => k.KongfuName, m => m.Kungfu,
23              (k, m) => new { k.KongfuId, k.KongfuName, k.Lethality, Count = m.Count() }
24              )
25              .OrderBy(k => k.KongfuId);
26 
27 foreach (var ma in JoinOrGroupJoinMethod)
28     Console.WriteLine(ma.KongfuId + "  " + ma.KongfuName + "  " + ma.Lethality + "  " + ma.Count + "\n");
29 
30 
31 Console.ReadKey();

 

執行結果如下:

 

5. 分組操作符 

1) 返回值為 IEnumerable<IGrouping<TKey, TSource>> ,根據指定的鍵選擇器函式對序列中的元素進行分組。 

2)  返回值為 IEnumerable<TResult>,根據指定的鍵選擇器函式對序列中的元素進行分組,並且從每個組及其鍵中建立結果值。 

 示例五:按門派分類大俠,統計出各個門派大俠數量

 1 //分組Group
 2 var GroupItems=  from m in master                             
 3                  group m by m.Menpai into g
 4                  orderby g.Key
 5                  select new
 6                  {
 7                      Menpai = g.Key,         
 8                     Count= g.Count()
 9                  };
10 Console.WriteLine("分組Group(表示式):\n");
11 Console.WriteLine("門派 所在門派人數\n");
12 foreach (var ma in GroupItems)
13     Console.WriteLine(ma.Menpai + "  " + ma.Count + "\n");
14 
15 
16 var GroupItemsMethod = master.GroupBy(m => m.Menpai, (k, m) => new { Menpai = k, Count = m.Count() });
17 
18 Console.WriteLine("分組Group(擴充套件方法 寫法一):\n");
19 Console.WriteLine("門派 所在門派人數\n");
20 foreach (var ma in GroupItemsMethod)
21     Console.WriteLine(ma.Menpai + "  " + ma.Count + "\n");
22 
23 
24 var GroupItemsMethod2 = master.GroupBy(m => m.Menpai).Select((m) => new { Menpai = m.Key, Count = m.Count() });
25 
26 Console.WriteLine("分組Group(擴充套件方法 寫法二):\n");
27 Console.WriteLine("門派 所在門派人數\n");
28 foreach (var ma in GroupItemsMethod2)
29     Console.WriteLine(ma.Menpai + "  " + ma.Count + "\n");
30 
31 Console.ReadKey();

執行結果如下:

 6.量詞操作符

如果元素序列滿足指定的條件,量詞操作符就返回布林值。

1)Any:確定序列是否包含任何元素;或確定序列中的任何元素是否都滿足條件。

2)All:確定序列中的所有元素是否滿足條件。

3)Contains:確定序列是否包含指定的元素。

學到這裡,再學習量詞操作符,就相當簡單了,直接看程式碼:

 1 //量詞操作符(Any.All,Contains)
 2 //先初始化一個物件備用
 3 var AnyItems = from m in master
 4                where m.Kungfu == "葵花寶典"
 5                select new { m.Name, m.Menpai, m.Kungfu };
 6 
 7 Console.WriteLine("練葵花寶典的所有大俠名單\n");
 8 Console.WriteLine("名字     門派   研習武學\n");
 9 foreach (var m in AnyItems)
10     Console.WriteLine(m.Name + "   " + m.Menpai + "   " + m.Kungfu+"\n");
11 //使用Any (確定序列是否包含任何元素;或確定序列中的任何元素是否都滿足條件)。
12 var any = AnyItems.Any(m => m.Menpai == "明教");
13 Console.WriteLine("是否研習葵花寶典有明教中人?"+any);
14 //返回True,說練習葵花寶典的大俠中有明教的人
15 
16 //All(確定序列中的所有元素是否滿足條件)
17 var all = AnyItems.All(m => m.Menpai == "明教");
18 Console.WriteLine("是否研習葵花寶典的全是明教中人?"+all);
19 
20 //Contains(確定序列中是否包含指定的元素)
21 //先宣告兩個元素
22 var OuYangFeng = new MartialArtsMaster() { Id = 13, Name = "歐陽鋒", Age = 50, Menpai = "白駝山莊", Kungfu = "蛤蟆功", Level = 10 };
23 var HuanYaoShi = master[9];
24 //使用Contains判斷是否存在
25 var IsOuYangFeng = master.Contains(OuYangFeng);
26 var IsHuangYaoShi = master.Contains(HuanYaoShi as MartialArtsMaster);
27 Console.WriteLine("大俠名單中是否存在歐陽鋒?" + IsOuYangFeng);
28 Console.WriteLine("大俠名單中是否存在黃藥師?" + IsHuangYaoShi);
29 Console.ReadKey();

執行結果如下:

 7.分割槽操作符

需要放在查詢的“最後”,返回集合的一個子集。

1)        Take:從序列的開頭返回指定數量的連續元素。

2)        TakeWhile:只要滿足指定的條件,就會返回序列的元素。

3)        Skip:跳過序列中指定數量的元素,然後返回剩餘的元素。

4)        SkipWhile:只要滿足指定的條件,就跳過序列中的元素,然後返回剩餘元素。

例項:我們使用分割槽操作符對各位大俠進行分頁(每頁5個)

 1 //分割槽操作符
 2 //我們在操作之前,先增加一些元素
 3 //增加幾個武林高手
 4 master.Add(new MartialArtsMaster() { Id = 12, Name = "肖峰", Age = 33, Menpai = "丐幫", Kungfu = "降龍十八掌", Level = 9 });
 5 master.Add(new MartialArtsMaster() { Id = 13, Name = "段譽", Age = 23, Menpai = "天龍寺", Kungfu = "六脈神劍", Level = 7 });
 6 master.Add(new MartialArtsMaster() { Id = 14, Name = "虛竹", Age = 26, Menpai = "逍遙派", Kungfu = "北冥神功", Level = 9 });
 7 master.Add(new MartialArtsMaster() { Id = 15, Name = "方正大師", Age = 23, Menpai = "少林寺", Kungfu = "七十二絕技", Level = 10 });
 8 master.Add(new MartialArtsMaster() { Id = 16, Name = "楊過", Age = 23, Menpai = "古墓派", Kungfu = "玉女心經", Level = 10 });
 9 master.Add(new MartialArtsMaster() { Id = 17, Name = "小龍女", Age = 23, Menpai = "古墓派", Kungfu = "玉女心經", Level = 10 });
10 
11 //增加幾個武學
12 kongfu.Add(new Kongfu() { KongfuId = 7, KongfuName = "六脈神劍", Lethality = 100 });
13 kongfu.Add(new Kongfu() { KongfuId = 8, KongfuName = "北冥神功", Lethality = 100 });
14 kongfu.Add(new Kongfu() { KongfuId = 9, KongfuName = "七十二絕技", Lethality = 100 });
15 kongfu.Add(new Kongfu() { KongfuId = 10, KongfuName = "玉女心經", Lethality = 95 });
16 
17 int pageSize = 5; //每頁數量 5
18 int pageNumber = (int)Math.Ceiling(master.Count() / (double)pageSize); //計算總頁數
19 Console.WriteLine("使用分割槽操作符進行分頁\n");
20 Console.WriteLine("大俠總數:" + master.Count() + "    總頁數:" + pageNumber + "    每頁:" + pageSize + "\n");
21 for (int i = 0; i < pageNumber;i++ )
22 {
23     Console.WriteLine("*************************第:" + (i+1).ToString() + "頁*************************\n");
24     var pageMaster = (
25                        from m in master
26                        join k in kongfu on m.Kungfu equals k.KongfuName
27                        orderby m.Level * k.Lethality descending
28                        select new { m.Name, m.Menpai, m.Kungfu, k.Lethality, m.Level, Kill = m.Level * k.Lethality }
29                     ).Skip(i * pageSize).Take(pageSize);
30     Console.WriteLine("姓名    門派     武功   武功殺傷力  修煉等級  總武力\n");
31     foreach (var m in pageMaster)
32         Console.WriteLine(m.Name + " " + m.Menpai + " " + m.Kungfu + " " + m.Lethality + " " + m.Level + " " + m.Kill + "\n");
33     Console.WriteLine();
34 }

執行結果如下:

8.集合操作符

1)Union:並集,返回兩個序列的並集,去掉重複元素。

2)Concat:並集,返回兩個序列的並集。

3)Intersect:交集,返回兩個序列中都有的元素,即交集。

4)Except:差集,返回只出現在一個序列中的元素,即差集。

 例項:華山派和明教中都使用葵花寶典的大俠

 1 //集合操作符
 2 //例項:華山派和明教中都使用葵花寶典的大俠
 3 var ItemsIntersect = (
 4              from m in master
 5              where m.Menpai == "華山" || m.Menpai=="明教"
 6              select m
 7            ).Intersect(
 8              from m in master
 9              where m.Kungfu == "葵花寶典"
10              select m
11            );
12 foreach (var m in ItemsIntersect)
13     Console.WriteLine(m.Name + " " + m.Menpai + " " + m.Kungfu);

執行結果如下:

9. 元素操作符

這些元素操作符僅返回一個元素,不是IEnumerable<TSource>。(預設值:值型別預設為0,引用型別預設為null)

1)First:返回序列中的第一個元素;如果是空序列,此方法將引發異常。

2)FirstOrDefault:返回序列中的第一個元素;如果是空序列,則返回預設值default(TSource)。

3)Last:返回序列的最後一個元素;如果是空序列,此方法將引發異常。

4)LastOrDefault:返回序列中的最後一個元素;如果是空序列,則返回預設值default(TSource)。

5)Single:返回序列的唯一元素;如果是空序列或序列包含多個元素,此方法將引發異常。

6)SingleOrDefault:返回序列中的唯一元素;如果是空序列,則返回預設值default(TSource);如果該序列包含多個元素,此方法將引發異常。

7)ElementAt:返回序列中指定索引處的元素,索引從0開始;如果索引超出範圍,此方法將引發異常。

8)ElementAtOrDefault:返回序列中指定索引處的元素,索引從0開始;如果索引超出範圍,則返回預設值default(TSource)。

10.  合計操作符

1)Count:返回一個 System.Int32,表示序列中的元素的總數量。

2)LongCount:返回一個 System.Int64,表示序列中的元素的總數量。

3)Sum:計算序列中元素值的總和。

4)Max:返回序列中的最大值。

5)Min:返回序列中的最小值。

6)Average:計算序列的平均值。

7)Aggregate:對序列應用累加器函式。

      Aggregate比較複雜,所以只列出Aggregate示例。

      Aggregate的第一個引數是演算法的種子,即初始值。第二個引數是一個表示式,用來對每個元素進行計算(委託第一個引數是累加變數,第二個引數當前項)。第三個引數是一個表示式,用來對最終結果進行資料轉換。

示例:

1 int[] numbers = { 1, 2, 3 };
2 // 1+2+3 = 6
3 int y = numbers.Aggregate((prod, n) => prod + n);
4 // 0+1+2+3 = 6
5 int x = numbers.Aggregate(0, (prod, n) => prod + n);
6 // (0+1+2+3)*2 = 12
7 int z = numbers.Aggregate(0, (prod, n) => prod + n, r => r * 2);

11.轉換操作符

1)Cast:將非泛型的 IEnumerable 集合元素轉換為指定的泛型型別,若型別轉換失敗則丟擲異常。

2)ToArray:從 IEnumerable<T> 建立一個陣列。

3)ToList:從 IEnumerable<T> 建立一個 List<T>。

4)ToDictionary:根據指定的鍵選擇器函式,從 IEnumerable<T> 建立一個 Dictionary<TKey,TValue>。

5)ToLookup:根據指定的鍵選擇器函式,從 IEnumerable<T> 建立一個 System.Linq.Lookup<TKey,TElement>。

6)DefaultIfEmpty:返回指定序列的元素;如果序列為空,則返回包含型別引數的預設值的單一元素集合。

7)   AsEnumerable:返回型別為 IEnumerable<T> 。用於處理LINQ to Entities操作遠端資料來源與本地集合的協作。

12. 生成操作符

生成操作符返回一個新的集合。(三個生成操作符不是擴充套件方法,而是返回序列的正常靜態方法)

1 ) Empty:生成一個具有指定型別引數的空序列 IEnumerable<T>。

2)  Range:生成指定範圍內的整數的序列 IEnumerable<Int32>。

3)  Repeat:生成包含一個重複值的序列 IEnumerable<T>。 

13.  Linq to Objects的延遲計算

Linq查詢的延遲計算原理:通過給LINQ擴充套件方法傳遞方法委託,作為yield迭代器的主體,讓遍歷執行到MoveNext()時才執行耗時的指令。

 整理Linq to Objects中運算子延遲計算特性

按字母順序整理:

具有延遲計算的運算子

Cast,Concat,DefaultIfEmpty,Distinct,Except,GroupBy,GroupJoin,Intersect 
,Join,OfType,OrderBy,OrderByDescending,Repeat,Reverse,Select,SelectMany,Skip
,SkipWhile,Take,TakeWhile,ThenBy,ThenByDescending,Union,Where,Zip

立即執行的運算子

Aggregate,All,Any,Average,Contains,Count,ElementAt,ElementAtOrDefault 
,Empty,First,FirstOrDefault,Last,LastOrDefault,LongCount,Max,Min,Range 
,SequenceEqual,Single,SingleOrDefault,Sum,ToArray,ToDictionary,ToList,ToLookup

特殊的AsEnumerable運算子,用於處理LINQ to Entities操作遠端資料來源,將IQueryable遠端資料立即轉化為本地的IEnumerable集合。若AsEnumerable接收引數是IEnumerable記憶體集合則什麼都不做。

 14.總結

在寫這節的時候,參考了不少部落格園還有網上的貼子,在此表示感謝。

LINQ學習還只是開始,但是有了上面兩節的學習,我們再進行深入學習,就不會不知所措了。

本節主要學習了LINQ to Objects在查詢中的使用方法。

 ============================================================================================== 

返回目錄

 <如果對你有幫助,記得點一下推薦哦,有不明白的地方或寫的不對的地方,請多交流>
 

QQ群:467189533

==============================================================================================  

相關文章