第十六章 LINQ資料查詢技術 16.1 LINQ基本概念 16.3 LINQ to Objects 16.2 LINQ查詢運算式的使用 16.4 LINQ to SQL 備註:可依進度點選小節
16.1 LINQ 基本概念 在 .NET Framework 3.5 新版功能中,最具影響力 莫過於 LINQ:Language Integrated Query語言 整合式查詢。 LINQ 是在 .NET 程式語言中加入查詢資料的能力。 .NET 語言如 VB 或 C# 可用查詢運算式語法來擴充 資料查詢能力。 查詢運算式語法和 SQL 陳述式類似,透過整合開發 環境智慧輸入功能,更能方便撰寫 LINQ查詢運算式 比撰寫單純 SQL 查詢字串更加方便。
使用 LINQ 資料查詢技術最大好處是讓程式設計師 能使用一致性的語法來查詢不同的資料來源。 如查詢物件集合、陣列、XML、SQL Server 資料庫、DataSet…等,讓程式設計師不需要再學習不同的資料查詢技術以縮短學習曲線。
LINQ 依適用對象分成下列幾種技術類型: 1. LINQ to Objects 可查詢實作 IEnumerable或IEnumerable<T> 介面的集合物件, 如陣列、List、集合、檔案物件的查詢、排序…等。 2. LINQ to SQL 可查詢實作 IQueryable<T> 介面的物件,也可直接對 SQL Server 資料庫做查詢與資料編輯。 3. LINQ to DataSet 可查詢記憶體內的 DataSet 或 DataTable。 4. LINQ to XML 以前要查詢或排序 XML文件必須透過 XPath 或 XQuery, 透過 LINQ to XML 查詢技術可查詢或排序 XML 文件, 且 LINQ to XML 使用方式與 LINQ to Objects、LINQ to SQL 和 LINQ to DataSet 非常類似。
16.2 LINQ查詢運算式的使用 LINQ 查詢運算式是以 from 子句啟始來指定範圍 變數以及所要查詢的集合。 這時會將集合內的元素(或陣列)逐一巡覽放到範圍 變數中進行查詢。 可用 orderby 子句排序 使用 where 設定欲查詢條件。 使用 select 子句定義查詢結果的欄位。 最後將查詢結果傳回給等號左邊指定的變數。 無論是查詢或排序陣列中元素、DataSet、DataTable、XML、集合物件或 SQL Server 資料庫…等資料來源,LINQ 查詢運算式的基本結構都很類似。
var 變數 = from [type] 範圍變數 in 集合 orderby 欄位名稱1 [ascending | descending][, 欄位名稱2 […] ] where <條件> select new {[別名1=]欄位名稱 [, [別名2=欄位名稱2 […] ] ]} ;
var 關鍵字只能在方法範圍內使用。 使用 var 所宣告的變數是屬於隱含型別的區域變數。 編譯器會自行判斷使用 var 所宣告變數內的資料,來決定該變數的資料型別。 資料型別定義完後即無法改變,這和 VB 6.0 自由型別變數是不一樣。 簡例: var i=5; var name=”Peter”; var money = new double {78.5, 69.23, 78.1}; var n=5; n = “Tom” ;
使用var宣告隱含型別的區域變數讓編譯器決定變數 的資料型別。 最主要原因是無法知道 LINQ 查詢運算式所查詢的結果 是陣列、物件、集合、DataSet 或 XML 文件 必須將 LINQ 查詢運算式的查詢結果指定給var所宣告 隱含型別的區域變數來決定查詢結果的資料型別。 撰寫 LINQ 查詢運算式基本上包含下列三個步驟: 定義資料來源 撰寫LINQ查詢運算式 執行查詢。
建立Salary 薪資陣列共有 Salary[0]~Salary[4] 5 個陣列元素 練習使用 LINQ查 詢運算式對 Salary 陣列做遞增 排序、遞減排序、找出大於 30,000 的薪資、找出 小於等於 30,000 的薪資、計算薪資平均五種狀況 作查詢,並將查詢結果顯示在表單的 richTextBox1 控制項上面。
找出 Salary 薪資陣列中大於 30,000 薪資: 問題分析 Step1 定義資料來源 因 Salary 薪資陣列要提供給多個事件處理函式一起共用,在事件 處理函式外建立 Salary 薪資陣列,做為 LINQ 查詢資料來源。 int[] Salary = new int[] { 50000, 80000, 20000, 30000, 45000 }; Step2 撰寫查詢運算式 找出 Salary 薪資陣列中大於 30,000的薪資,做遞增排序, 最後 將查詢結果傳回 result 隱含型別區域變數。 var result = from s in Salary orderby s descending where s > 30000 select s;
透過 foreach{…} 敘述將陣列(集合物件)中的元素 列舉出並顯示在 richTextBox1 控制項上。 Step3 執行查詢 透過 foreach{…} 敘述將陣列(集合物件)中的元素 列舉出並顯示在 richTextBox1 控制項上。 foreach (var s in result) { richTextBox1.Text += s.ToString() + "\n"; } 上述執行後,richTextBox1 上會顯示 「80000, 50000, 45000」。
LINQ 提供很多擴充方法,常用擴充方法說明如下:
Step1 設計輸出入介面
FileName : Linq1.sln 01 int[] Salary = new int[] { 50000, 80000, 20000, 30000, 45000 }; 02 // 表單載入時執行此事件處理函式 03 private void Form1_Load(object sender, EventArgs e) 04 { 05 richTextBox1.Text = ""; 06 var result = from s in Salary select s; 07 int i = 0; 08 foreach (var s in result) 09 { 10 i++; 11 richTextBox1.Text += i.ToString() + ". " + s.ToString() + "\n"; 12 } 13 richTextBox1.Text += "平均薪資:" + result.Average().ToString(); 14 }
15 // 按遞增排序鈕執行此事件處理函式 16 private void btnSortIncrease_Click(object sender, EventArgs e) 17 { 18 richTextBox1.Text = "遞增排序:\n"; 19 var result = from s in Salary // 將Salary陣列所有元素遞增排序 orderby s ascending select s; 20 int i = 0; 21 foreach (var s in result) // 將s中所有資料逐一顯示在richTextBox1控制項上 22 { 23 i++; 24 richTextBox1.Text += i.ToString() + ". " + s.ToString() + "\n"; 25 } 26 } 27 // 按遞減排序鈕執行此事件處理函式 28 private void btnSortDecrease_Click(object sender, EventArgs e) 29 { 30 richTextBox1.Text = "遞減排序:\n"; 31 var result = from s in Salary // 將Salary陣列所有元素遞減排序 orderby s descending 32 int i = 0;
33 foreach (var s in result) 34 { 35 i++; 36 richTextBox1. Text += i 33 foreach (var s in result) 34 { 35 i++; 36 richTextBox1.Text += i.ToString() + ". " + s.ToString() + "\n"; 37 } 38 } 39 // 按大於30000鈕執行此事件處理函式 40 private void btnGreater30000_Click(object sender, EventArgs e) 41 { 42 richTextBox1.Text = "大於30000薪資:\n"; 43 var result = from s in Salary orderby s descending where s > 30000 select s; 44 int i = 0; 45 foreach (var s in result) 46 { 47 i++; 48 richTextBox1.Text += i.ToString() + ". " + s.ToString() + "\n"; 49 } 50 richTextBox1.Text+="共 " + result.Count().ToString() + " 人"; 51 } 52
52 // 按小於等於30000鈕執行此事件處理函式 53 private void btnSmaller30000_Click(object sender, EventArgs e) 54 { 55 richTextBox1.Text = "小於等於30000薪資:\n"; 56 var result = from s in Salary orderby s descending where s <= 30000 select s; 57 int i = 0; 58 foreach (var s in result) 59 { 60 i++; 61 richTextBox1.Text += i.ToString() + ". " + s.ToString() + "\n"; 62 } 63 richTextBox1.Text+="共 "+ result.Count().ToString()+"人"; 64 } 65 // 按平均薪資鈕執行此事件處理函式 66 private void btnAvg_Click(object sender, EventArgs e) 67 { 68 Form1_Load(sender, e); 69 }
16.3 LINQ to Objects LINQ to Objects 可透過 LINQ 查詢運算式來查詢實作 IEnumerable 或 IEnumerable <T> 介面集合的物件。 您可用 LINQ 來查詢任何可列舉集合,如上一範例的陣列、List<T>、ArrayList<T> 或是使用者定義的物件集合。
先定義 Employee 類別有員工編號、姓名、信箱、雇用日期 薪資、是否已婚 六個屬性。 在表單載入Form1_Load事件處理函式建立emp[0]~emp[4] 五個員工物件記錄後,接著透過員工編號、薪資、雇用 日期來做遞增排序,並將排序的結果顯示在豐富文字方塊 上。也可在文字方塊內輸入要查詢的員工編號並按 鈕來查詢是否有該位員工,執行結果如下圖。
下列分別按 、 、 鈕所進行的 遞增排序結果。
Step1 建立名稱為 Linq2 的 Windows Form 應用程式專案。 Step2 設計Employee.cs 員工類別檔 在Employee類別定義員工編號、姓名、信箱、薪資、 員工編號、是否已婚共有六個屬性:
02 using System.Collections.Generic; 03 using System.Linq; FileName : Employee.cs 01 using System; 02 using System.Collections.Generic; 03 using System.Linq; 04 using System.Text; 05 06 namespace Linq2 07 { 08 class Employee 09 { 10 public string 員工編號 { get; set; } 11 public string 姓名 { get; set; } 12 public string 信箱 { get; set; } 13 public int 薪資{get; set;} 14 public DateTime 雇用日期{get; set;} 15 public bool 是否已婚 { get; set; } 16 } 17 }
Step3 設計 Form1.cs 表單輸出入介面
FileName : Form1.cs 01 Employee[] emp = new Employee[5]; 02 // 表單載入時執行此事件處理函式 03 private void Form1_Load(object sender, EventArgs e) 04 { 05 emp[0] = new Employee { 姓名 = "菜一林", 信箱 = "jolin@yahoo.com.tw", 是否已婚 = false, 員工編號 = "E001", 雇用日期 = new DateTime(2007, 1, 1), 薪資 = 65000 }; 06 emp[1] = new Employee { 姓名 = "羅字祥", 信箱 = "kklao@yahoo.com.tw", 是否已婚 = true, 員工編號 = "E002", 雇用日期 = new DateTime(2006, 1, 1), 薪資 = 75000 }; 07 emp[2] = new Employee { 姓名 = "周傑輪", 信箱 = "jjjaa@yahoo.com.tw", 是否已婚 = false, 員工編號 = "E003", 雇用日期 = new DateTime(2008, 1, 1), 薪資 = 55000 }; 08 emp[3] = new Employee { 姓名 = "王建名", 信箱 = "wange@yahoo.com.tw", 是否已婚 = true, 員工編號 = "E004", 雇用日期 = new DateTime(2006, 5, 3), 薪資 = 105000 };
09 emp[4] = new Employee { 姓名 = "李五六", 信箱 = "finde@yahoo. com 09 emp[4] = new Employee { 姓名 = "李五六", 信箱 = "finde@yahoo.com.tw", 是否已婚 = false, 員工編號 = "E005", 雇用日期 = new DateTime(2008, 5, 2), 薪資 = 45000 }; 10 richTextBox1.Text = "編號\t姓名\t信箱\t\t\t雇用日期\t薪資\t是否已婚\n"; 11 richTextBox1.Text += "========================================================\n"; 12 var result = from p in emp select p; 13 foreach (var p in result) 14 { 15 richTextBox1.Text += p.員工編號 + "\t" + p.姓名 + "\t" + p.信箱 + "\t" + p.雇用日期.ToShortDateString() + " \t" + p.薪資.ToString() + "\t" + p.是否已婚.ToString() + "\n"; 16 } 17 }
18 // 按員工編號鈕執行此事件處理函式 19 private void btnEmpId_Click(object sender, EventArgs e) 20 { 21 richTextBox1.Text = "編號\t姓名\t信箱\t\t\t雇用日期\t薪資\t是否已婚\n"; 22 richTextBox1.Text += "========================================================\n"; 23 var result = from p in emp orderby p.員工編號 ascending select p; 24 foreach (var p in result) 25 { 26 richTextBox1.Text += p.員工編號 + "\t" + p.姓名 + "\t" + p.信箱 + "\t" + p.雇用日期.ToShortDateString() + " \t" + p.薪資.ToString() + "\t" + p.是否已婚.ToString() + "\n"; 27 } 28 }
29 // 按薪資鈕執行此事件處理函式 30 private void btnSalary_Click(object sender, EventArgs e) 31 { 32 richTextBox1.Text = "編號\t姓名\t信箱\t\t\t雇用日期\t薪資\t是否已婚\n"; 33 richTextBox1.Text += "========================================================\n"; 34 var result = from p in emp orderby p.薪資 ascending select p; 35 foreach (var p in result) 36 { 37 richTextBox1.Text += p.員工編號 + "\t" + p.姓名 + "\t" + p.信箱 + "\t" + p.雇用日期.ToShortDateString() + " \t" + p.薪資.ToString() + "\t" + p.是否已婚.ToString() + "\n"; 38 } 39 }
40 // 按雇用日期鈕執行此事件處理函式 41 private void btnDate_Click(object sender, EventArgs e) 42 { 43 richTextBox1.Text = "編號\t姓名\t信箱\t\t\t雇用日期\t薪資\t是否已婚\n"; 44 richTextBox1.Text += "========================================================\n"; 45 var result = from p in emp orderby p.雇用日期 ascending select p; 46 foreach (var p in result) 47 { 48 richTextBox1.Text += p.員工編號 + "\t" + p.姓名 + "\t" + p.信箱 + "\t" + p.雇用日期.ToShortDateString() + " \t" + p.薪資.ToString() + "\t" + p.是否已婚.ToString() + "\n"; 49 } 50 }
51 // 按單筆查詢鈕執行此事件處理函式 52 private void btnSelEmpById_Click(object sender, EventArgs e) 53 { 54 richTextBox1.Text = ""; 55 var result = from p in emp where p.員工編號 == txtEmpId.Text select new { p.員工編號, p.姓名, p.信箱, p.薪資 }; 61 if (result.Count() == 0) //判斷查詢的結果是否為零筆 62 { 63 MessageBox.Show("沒有此員工"); 64 return; 65 } 66 foreach (var p in result) 67 { 68 richTextBox1.Text += "編號:" + p.員工編號; 69 richTextBox1.Text += "\n姓名:" + p.姓名; 70 richTextBox1.Text += "\n信箱:" + p.信箱; 71 richTextBox1.Text += "\n薪資:" + p.薪資; 72 } 73 }
16.4 LINQ To SQL LINQ to SQL 的技術是以 ADO .NET 資料提供者模型所提供的服務為基礎。 LINQ to SQL是物件模型(Object Model)與關連式資料庫Mapping的技術 簡單說就是資料庫、資料表、資料列、資料欄位、主鍵及關聯 都可直接對應至程式設計中的物件,在撰寫新增、修改、刪除 及查詢資料庫程式時,完全不用撰寫 SQL 語法的 SELECT、INSERT、DELETE、UPDATE 陳述式,可不用處理資料庫 程式設計的細節,以直覺物件導向程式來直接撰寫資料庫應用 程式。 目前 LINQ to SQL 技術只支援微軟的 SQL Server 資料庫, 並不支援其他廠商資料庫。
下表即是 LINQ to SQL 物件模型對應至資料庫的物件 類別對應至資料表 類別屬性對應至資料行(資料欄位) 類別方法對應至SQL Server資料庫函式或預存程序。
使用 ORM 設計工具動態產生可對應至Database 使用 ORM 設計工具動態產生可對應至Database.mdf 實體資料庫的 DataClasses1DataContext 類別程式碼 透過 DataClasses1DataContext 類別產生的dc物件及 配合LINQ to SQL查詢運算式來查詢「員工」資料表, 可依員工編號、薪資、雇用日期做遞增排序,並將排序 結果顯示dataGridView1;可查詢最高薪資、最低薪資 平均薪資及薪資加總。也可在文字方塊內輸入要查詢的 員工編號並按 鈕來查詢是否有該位員工。
Step1 建立名稱為 Linq3 的 Windows Form 應用程式專案。
Step3 建立 LINQ to SQL 類別檔
FileName : Linq3.sln 01 DataClasses1DataContext dc = new DataClasses1DataContext(); 02 // 表單載入時執行此事件處理函式 03 private void Form1_Load(object sender, EventArgs e) 04 { 05 var result1 = from p in dc.員工 select p; 06 dataGridView1.DataSource = result1; 08 var result2 = from p in dc.員工 select p.薪資; 09 lblShow.Text = "最高薪資:" + result2.Max().ToString() + "\n最低薪資:" + result2.Min().ToString() + "\n平均薪資:" + result2.Average().ToString() + "\n薪資加總:" + result2.Sum ().ToString(); 10 lblShow.BorderStyle = BorderStyle.Fixed3D; 11 lblShow.BackColor = Color.Pink; 12 }
13 // 按員工編號鈕執行此事件處理函式 14 private void btnEmpId_Click(object sender, EventArgs e) 15 { 16 var result = from p in dc.員工 orderby p.員工編號 ascending select p; 17 dataGridView1.DataSource = result; 18 } 19 // 按薪資鈕執行此事件處理函式 20 private void btnSalary_Click(object sender, EventArgs e) 21 { 22 var result = from p in dc.員工 orderby p.薪資 ascending 23 dataGridView1.DataSource = result; 24 }
25 // 按雇用日期鈕執行此事件處理函式 26 private void btnDate_Click(object sender, EventArgs e) 27 { 28 var result = from p in dc.員工 orderby p.雇用日期 ascending select p; 29 dataGridView1.DataSource = result; 30 } 31 // 按單筆查詢鈕執行此事件處理函式 32 private void btnSelEmpById_Click(object sender, EventArgs e) 33 { 34 var result = from p in dc.員工 where p.員工編號 == txtEmpId.Text 37 if (result.Count() == 0) 38 { 39 MessageBox.Show("沒有此員工"); 40 return; 41 } 42 foreach (var p in result) 43 { 44 MessageBox.Show("編號:" + p.員工編號 + "\n姓名:" + p.姓名 + "\n信箱:" + p.信箱 + "\n薪資:" + p.薪資); 45 } 46 }