本筆記摘抄自:https://www.cnblogs.com/liqingwen/p/5832322.html,記錄一下學習程序以備后續查用,
LINQ 簡介:
語言集成查詢(LINQ)是Visual Studio 2008和.NET Framework 3.5版中引入的一項創新功能,
傳統上,針對資料的查詢都是以簡單的字串表示,而沒有編譯時型別檢查或IntelliSense支持,此外,您還必須針對以下各種資料源學習一種不同的查詢
語言:SQL資料庫、XML檔案、各種Web服務等,通過LINQ,可以使用語言關鍵字和熟悉的運算子針對強型別化物件集合撰寫查詢,

在Visual Studio中,可以為以下資料源撰寫LINQ查詢:SQL Server資料庫、XML檔案、ADO.NET資料集以及支持 IEnumerable或泛型 IEnumerable<T>
介面的任意物件集合,使用要求:專案 ≥ .NET Framework 3.5,
一、LINQ查詢
查詢是一種從資料源檢索資料的運算式,隨著時間的推移,人們已經為各種資料源開發了不同的語言,例如,用于關系資料庫的SQL和用于XML的XQuery,
因此,開發人員不得不針對他們必須支持的每種資料源或資料格式而學習新的查詢語言,LINQ通過提供一種跨資料源和資料格式使用資料的一致模型,簡化
了這一情況,在LINQ查詢中,始侄訓用到物件,可以使用相同的編碼模式來查詢和轉換XML檔案、SQL資料庫、ADO.NET資料集、.NET集合中的資料以及
對其有LINQ提供程式可用的任何其他格式的資料,
1.1 查詢操作的三個部分
操作三部曲:①取資料源 ②創建查詢 ③執行查詢
下面代碼演示LINQ to OBJECT:
class Program { static void Main(string[] args) { #region LINQ to OBJECT //查詢三部曲:1、獲取資料源 var nums = new int[7] { 0, 1, 2, 3, 4, 5, 6 }; //查詢三部曲:2、創建查詢 var query = from num in nums where (num % 2) == 0 select num; //查詢三部曲:3、執行查詢 foreach (var num in query) { Console.Write($"{num} "); } Console.Read(); #endregion } }View Code
運行結果如下:

下圖顯示了完整的查詢操作,在LINQ中,查詢的執行與查詢本身截然不同,換句話說,查詢本身指的是只創建查詢變數,不檢索任何資料,

1.2 資料源
在上一個示例中(LINQ to OBJECT),由于資料源是陣列,因此它隱式支持泛型 IEnumerable<T> (可列舉)介面,支持 IEnumerable<T> 或派生介面(如
泛型IQueryable<T>)的型別稱為可查詢型別,
可查詢型別不需要進行修改或特殊處理就可以用作LINQ資料源,如果源資料還沒有作為可查詢型別出現在記憶體中,則LINQ提供程式必須以此方式表示源數
據,例如,LINQ to XML將XML檔案加載到可查詢的 XElement 型別中,
下面代碼演示LINQ to XML:
創建一個Test.xml檔案,放在主目錄下,
<DocumentElement> <Category> <MO_NO>MOA1911070001</MO_NO> <MRP_NO>8198712090963008</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110701</BIL_NO> </Category> <Category> <MO_NO>MOA1911070002</MO_NO> <MRP_NO>8193000000003172</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110702</BIL_NO> </Category> <Category> <MO_NO>MOA1911070003</MO_NO> <MRP_NO>8193002043133003</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110702</BIL_NO> </Category> <Category> <MO_NO>MOA1911070004</MO_NO> <MRP_NO>8193002043133004</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110702</BIL_NO> </Category> <Category> <MO_NO>MOA1911070005</MO_NO> <MRP_NO>8193002043133005</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110702</BIL_NO> </Category> <Category> <MO_NO>MOA1911070006</MO_NO> <MRP_NO>8198922092971001</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110703</BIL_NO> </Category> <Category> <MO_NO>MOA1911070007</MO_NO> <MRP_NO>8198922092971002</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110703</BIL_NO> </Category> <Category> <MO_NO>MOA1911070008</MO_NO> <MRP_NO>8198922092971010</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110703</BIL_NO> </Category> <Category> <MO_NO>MOA1911070009</MO_NO> <MRP_NO>8198922092971200</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110703</BIL_NO> </Category> <Category> <MO_NO>MOA1911070010</MO_NO> <MRP_NO>8199862094443008</MRP_NO> <QTY>1.00000000</QTY> <BIL_NO>MPA19110704</BIL_NO> </Category> </DocumentElement>View Code
class Program { static void Main(string[] args) { #region LINQ to XML var xe = XElement.Load(@"..\..\Test.xml"); var query = from item in xe.Descendants("Category") select item; foreach (var item in query) { Console.WriteLine($"MO_NO={item.Element("MO_NO").Value}"); } Console.Read(); #endregion } }View Code
運行結果如下:

在LINQ to SQL中,首先需要生成物件模型映射到關系資料庫,然后針對這些物件撰寫查詢,由LINQ to SQL在運行時處理與資料庫的通信,
準備一:下文中用到的Northwind資料庫,下載地址為:https://www.microsoft.com/en-us/download/confirmation.aspx?id=23654
準備二:若本機沒有安裝LINQ to SQL工具,可參考安裝教程:https://blog.csdn.net/u011176794/article/details/90287293
準備三:添加新建項,選擇LINQ to SQL類,命名為Sample,
準備四:服務器資源管理器的資料連接中,右鍵添加連接,依導向連接至SQL Server資料庫,
準備五:將相關資料表拖至Sample.dbml中并保存,
準備六:添加參考System.Data.Linq,
下面代碼演示LINQ to SQL:
class Program { static void Main(string[] args) { #region LINQ to SQL var db = new SampleDataContext(); var query = from cust in db.Customers where cust.City == "London" select cust; foreach (var item in query) { Console.WriteLine($"CustomerID={item.CustomerID}"); } Console.Read(); #endregion } }View Code
運行結果如下:

查詢指定要從資料源中檢索的資訊,可以指定在回傳這些資訊之前如何對其進行排序、分組和結構化, 查詢存盤在查詢變數中,并用查詢運算式進行初始
化,之前的示例中的查詢是從整數陣列中回傳所有的偶數,該查詢運算式包含三個子句:from、where和select,如果您熟悉SQL,您會注意到這些子句的
順序與SQL中的順序相反,from 子句指定資料源,where 子句指定應用篩選器,select 子句指定回傳的元素的型別,目前需要注意的是,在LINQ中,查詢
變數本身不執行任何操作并且不回傳任何資料,它只是存盤在以后某個時刻執行查詢時為生成結果而必需的資訊,
1.4 查詢執行
1.延遲執行
如前所述,查詢變數本身只是存盤查詢命令,實際的查詢執行會延遲到在foreach陳述句中回圈訪問查詢變數時發生,此概念稱為“延遲執行”,
2.強制立即執行
對一系列源元素執行聚合函式的查詢必須首先回圈訪問這些元素,Count、Max、Average和First就屬于此類查詢,由于查詢本身必須使用foreach以便
回傳結果,因此這些查詢在執行時不使用顯式foreach陳述句,另外還要注意,這些型別的查詢回傳單個值,而不是IEnumerable集合,
class Program { static void Main(string[] args) { #region LINQ查詢強制立即執行一 var nums = new int[7] { 0, 1, 2, 3, 4, 5, 6 }; var query = from num in nums where (num % 2) == 0 select num; var numCount = query.Count(); Console.WriteLine($"NumCount={numCount}"); Console.Read(); #endregion } }View Code 運行結果如下:
若要強制立即執行任意查詢并快取其結果,可以呼叫ToList<TSource>或ToArray<TSource>方法,
class Program { static void Main(string[] args) { #region LINQ查詢強制立即執行二 var nums = new int[7] { 0, 1, 2, 3, 4, 5, 6 }; var query2 = (from num in nums where (num % 2) == 0 select num).ToList(); var query3 = (from num in nums where (num % 2) == 0 select num).ToArray(); Console.WriteLine($"NumCount={query2.Count}"); Console.WriteLine($"NumCount={query3.Length}"); Console.Read(); #endregion } }View Code
運行結果如下:

二、基本 LINQ 查詢操作
2.1 獲取資料源:from
在LINQ查詢中,第一步是指定資料源,像在大多數編程語言中一樣,必須先宣告變數,才能使用它,在LINQ查詢中,最先使用from子句的目的是引入資料
源和范圍變數,
class Program { static void Main(string[] args) { #region LINQ to SQL var db = new SampleDataContext(); //query是IEnumerable<Cutsomer>型別 //資料源(db.customers)和范圍變數(cust) var query = from cust in db.Customers where cust.City == "London" select cust; foreach (var item in query) { Console.WriteLine($"CustomerID={item.CustomerID}"); } Console.Read(); #endregion } }View Code
范圍變數類似于foreach回圈中的迭代變數,但在查詢運算式中,實際上不發生迭代,執行查詢時,范圍變數將用作對customers中的每個后續元素的參考,
因為編譯器可以推斷cust的型別,所以您不必顯式指定此型別,
2.2 篩選:where
也許最常用的查詢操作是應用布爾運算式形式的篩選器,此篩選器使查詢只回傳那些運算式結果為true的元素,使用where子句生成結果,實際上,篩選器
指定從源序列中排除哪些元素,
您可以使用熟悉的C#邏輯AND(&&)和OR(||)運算子來根據需要在where子句中應用任意數量的篩選運算式,
class Program { static void Main(string[] args) { #region LINQ 篩選:where var db = new SampleDataContext(); var query1 = from cust in db.Customers where cust.City == "London" && cust.CustomerID == "AROUT" select cust; var query2 = from cust in db.Customers where cust.City == "London" || cust.City == "Paris" select cust; foreach (var item in query1) { Console.WriteLine($"query1->City={item.City},CustomerID={item.CustomerID}"); } foreach (var item in query2) { Console.WriteLine($"query2->City={item.City},CustomerID={item.CustomerID}"); } Console.Read(); #endregion } }View Code
運行結果如下:

2.3 排序:orderby
通常可以很方便地將回傳的資料進行排序,orderby子句將使回傳的序列中的元素按照被排序的型別的默認比較器進行排序,
class Program { static void Main(string[] args) { #region LINQ 排序:orderby var db = new SampleDataContext(); var query = from cust in db.Customers where cust.City == "London" orderby cust.CustomerID select cust; foreach (var item in query) { Console.WriteLine($"CustomerID={item.CustomerID}"); } Console.Read(); #endregion } }View Code
運行結果如下:

2.4 分組:group
使用group子句,您可以按指定的鍵分組結果,
class Program { static void Main(string[] args) { #region LINQ 分組一:group var db = new SampleDataContext(); var query = from cust in db.Customers where cust.City == "London" || cust.City == "Paris" group cust by cust.City; foreach (var group in query) { Console.WriteLine(group.Key); foreach (var cust in group) { Console.WriteLine($"City={cust.City},CustomerID={cust.CustomerID}"); } } Console.Read(); #endregion } }View Code
運行結果如下:

在本例中,cust.City是鍵,
在使用group子句結束查詢時,結果采用串列的串列形式,串列中的每個元素是一個具有Key成員及根據該鍵分組的元素串列的物件,在回圈訪問生成組
序列的查詢時,您必須使用嵌套的foreach回圈,外部回圈用于回圈訪問每個組,內部回圈用于回圈訪問每個組的成員,
如果您必須參考組操作的結果,可以使用into關鍵字來創建可進一步查詢的識別符號,
class Program { static void Main(string[] args) { #region LINQ 分組二:group var db = new SampleDataContext(); var query = from cust in db.Customers where cust.City == "London" || cust.City == "Paris" group cust by cust.City into custGroup where custGroup.Count() > 2 orderby custGroup.Key select custGroup; foreach (var group in query) { Console.WriteLine(group.Key); foreach (var cust in group) { Console.WriteLine($"City={cust.City},CustomerID={cust.CustomerID}"); } } Console.Read(); #endregion } }View Code
運行結果如下:

2.5 聯接:join
聯接運算創建資料源中沒有顯式建模的序列之間的關聯,例如,您可以執行聯接來查找位于同一地點的所有客戶和經銷商,在LINQ中,join子句始終針對
物件集合而非直接針對資料庫表運行,
class Program { static void Main(string[] args) { #region LINQ 聯接:join var db = new SampleDataContext(); var qurey = from order in db.Orders join cust in db.Customers on order.CustomerID equals cust.CustomerID select new { order.OrderID, order.CustomerID, cust.ContactName }; foreach (var item in qurey.Take(5)) { Console.WriteLine($"OrderID={item.OrderID},CustomerID={item.CustomerID},ContactName={item.ContactName}"); } Console.Read(); #endregion } }View Code
運行結果如下:

2.6 選擇(投影):select
select子句生成查詢結果并指定每個回傳的元素的“形狀”或型別,
例如,您可以指定結果包含的是整個Customer物件、僅一個成員、成員的子集或者某個基于計算或新物件創建的完全不同的結果型別,當select子句生成除源
元素副本以外的內容時,該操作稱為“投影”,
三、使用 LINQ 進行資料轉換
語言集成查詢 (LINQ) 不但是檢索資料的利器,而且還是一個功能強大的資料轉換工具,通過使用LINQ查詢,您可以將源序列用作輸入,并采用多種方式修改
它以創建新的輸出序列,您可以通過排序及分組來修改該序列,而不必修改元素本身,但是,LINQ 查詢的最強大的功能是能夠創建新型別,這一功能在select子
句中實作,
3.1 將多個輸入聯接到一個輸出序列
/// <summary> /// 學生類 /// </summary> class Student { public string Name { get; set; } public int Age { get; set; } public string City { get; set; } public List<int> Scores { get; set; } } /// <summary> /// 教師類 /// </summary> class Teacher { public string Name { get; set; } public int Age { get; set; } public string City { get; set; } } class Program { static void Main(string[] args) { #region LINQ 將多個輸入聯接到一個輸出序列 //創建第一個資料源 var students = new List<Student>() { new Student() { Age = 19, City = "廣州", Name = "小A", Scores = new List<int>(){85,88,83,97} }, new Student() { Age = 19, City = "深圳", Name = "小B", Scores = new List<int>(){86,80,85,92} } }; //創建第二個資料源 var teachers = new List<Teacher>() { new Teacher() { Age = 30, City = "廣州", Name = "張A" }, new Teacher() { Age = 31, City = "廣州", Name = "李A" } }; //創建查詢 var query = ( from student in students where student.City == "廣州" select student.Name ).Concat ( from teacher in teachers where teacher.City == "廣州" select teacher.Name ); //執行查詢 foreach (var person in query) { Console.WriteLine(person); } Console.Read(); #endregion } }View Code
運行結果如下:

3.2 選擇各個源元素的子集
1. 若要只選擇源元素的一個成員,請使用點運算,
var query = from cust in db.Customers select cust.City;
2. 若要創建包含源元素的多個屬性的元素,可以使用具有命名物件或匿名型別的物件初始值設定項,
var query = from cust in db.Customer select new { cust.Name, cust.City };
3.3 將記憶體中的物件轉換為XML
/// <summary> /// 學生類 /// </summary> class Student { public string Name { get; set; } public int Age { get; set; } public string City { get; set; } public List<int> Scores { get; set; } } class Program { static void Main(string[] args) { #region LINQ 將記憶體中的物件轉換為XML //創建資料源 var students = new List<Student>() { new Student() { Age = 19, City = "廣州", Name = "小A", Scores = new List<int>(){85,88,83,97} }, new Student() { Age = 19, City = "深圳", Name = "小B", Scores = new List<int>(){86,80,85,92} } }; //創建查詢 var studentsToXml = new XElement ( "Root", from student in students let x = $"{student.Scores[0]},{student.Scores[1]},{student.Scores[2]},{student.Scores[3]}" select new XElement ( "student", new XElement("Name", student.Name), new XElement("Age", student.Age), new XElement("Scores", x) ) ); //執行查詢 Console.WriteLine(studentsToXml); Console.Read(); #endregion } }View Code
運行結果如下:

3.4 對源元素執行操作
輸出序列可能不包含源序列的任何元素或元素屬性,它可能是通過將源元素用作輸入引數計算出的值的序列,
class Program { static void Main(string[] args) { #region LINQ 對源元素執行操作 //資料源 double[] radius = { 1, 2, 3 }; //創建查詢 var query = from radiu in radius select $"{3.14 * radiu * radiu}"; //執行查詢 foreach (var item in query) { Console.WriteLine(item); } Console.Read(); #endregion } }View Code
運行結果如下:

四、LINQ 查詢操作的型別關系
LINQ 查詢操作在資料源、查詢本身及查詢執行中是強型別的,查詢中變數的型別必須與資料源中元素的型別和foreach陳述句中迭代變數的型別兼容,強型別可
以保證在編譯時捕獲型別錯誤,以便及時改正,
4.1 不轉換源資料的查詢
下圖演示不對資料執行轉換的LINQ to OBJECT查詢操作,源包含一個字串序列,查詢輸出也是一個字串序列,

①資料源的型別引數決定范圍變數的型別,
②select陳述句回傳Name屬性,而非完整的Customer物件,因為Name是一個字串,所以custNameQuery的型別引數是string,而非Customer,
③因為custNameQuery是一個字串序列,所以foreach回圈的迭代變數也必須是string,
4.2 轉換源資料的查詢
下圖演示對資料執行簡單轉換的LINQ to SQL查詢操作,查詢將一個Customer物件序列用作輸入,并只選擇結果中的Name屬性,因為Name是一個字串,
所以查詢生成一個字串序列作為輸出,

①資料源的型別引數決定范圍變數的型別,
②select陳述句回傳Name屬性,而非完整的Customer物件,因為Name是一個字串,所以custNameQuery的型別引數是string,而非Customer,
③因為custNameQuery是一個字串序列,所以foreach回圈的迭代變數也必須是string,
下圖演示另一種轉換,select 陳述句回傳只捕獲原始Customer物件的兩個成員的匿名型別,

①資料源的型別引數始終為查詢中的范圍變數的型別,
②因為select陳述句生成匿名型別,所以必須使用var隱式型別化查詢變數,
③因為查詢變數的型別是隱式的,所以foreach回圈中的迭代變數也必須是隱式的,
4.3 讓編譯器推斷型別資訊
您也可以使用關鍵字var,可用于查詢操作中的任何區域變數,但是,編譯器為查詢操作中的各個變數提供強型別,

五、LINQ 中的查詢語法和方法語法
我們撰寫的LINQ查詢語法,在編譯代碼時,CLR會將查詢語法轉換為方法語法,這些方法呼叫標準查詢運算子的名稱類似Where、Select、GroupBy、Join、
Max和Average,我們也是可以直接使用這些方法語法的,
查詢語法和方法語法語意相同,但是,許多人員發現查詢語法更簡單、更易于閱讀,某些查詢必須表示為方法呼叫,例如,必須使用方法呼叫表示檢索元素
的數量與指定的條件的查詢,還必須使用方法需要檢索元素的最大值在源序列的查詢,System.Linq命名空間中的標準查詢運算子的參考檔案通常使用方法語法,
5.1 標準查詢運算子擴展方法
class Program { static void Main(string[] args) { #region LINQ 標準查詢運算子擴展方法 var nums = new int[4] { 1, 2, 3, 4 }; //創建查詢運算式 var query1 = from num in nums where num % 2 == 0 orderby num descending select num; Console.WriteLine("Query1's result:"); foreach (var num in query1) { Console.WriteLine(num); } //使用方法進行查詢 var query2 = nums.Where(num => num % 2 == 0).OrderByDescending(num => num); Console.WriteLine("Query2's result:"); foreach (var num in query2) { Console.WriteLine(num); } Console.Read(); #endregion } }View Code
運行結果如下:

兩個示例的輸出是相同的,您可以看到兩種形式的查詢變數的型別是相同的:IEnumerable<T>,
若要了解基于方法的查詢,讓我們進一步地分析它,注意,在運算式的右側,where子句現在表示為對numbers物件的實體方法,在您重新呼叫該物件時其型別
為IEnumerable<int>,如果您熟悉泛型 IEnumerable<T> 介面,那么您就會了解,它不具有Where方法,但是,如果您在Visual Studio IDE中呼叫IntelliSense完成
串列,那么您不僅將看到Where方法,而且還會看到許多其他方法,如Select、SelectMany、Join 和Orderby,
下面是所有標準查詢運算子:

盡管看起來IEnumerable<T>似乎已被重新定義以包括這些附加方法,但事實上并非如此,這些標準查詢運算子都是作為“擴展方法”實作的,
5.2 Lambda 運算式
在前面的示例中,通知該條件運算式 (num % 2 == 0) 是作為行內引數,Where方法:Where (num => num % 2 == 0) 此行內運算式稱為lambda運算式,將代碼
撰寫為匿名方法或泛型委托或運算式樹是一種便捷的方法,否則撰寫起來就要麻煩得多,=>是lambda運算子,可讀為“goes to”,運算子左側的num是輸入變數,
與查詢運算式中的num相對應,編譯器可推斷num的型別,因為它了解numbers是泛型IEnumerable<T>型別,Lambda運算式與查詢語法中的運算式或任何其他C#
運算式或陳述句中的運算式相同,它可以包括方法呼叫和其他復雜邏輯,“回傳值”就是運算式結果,
5.3 查詢的組合性
在上面的代碼示例中,請注意OrderBy方法是通過在對Where的呼叫中使用點運算子來呼叫的,Where生成篩選序列,然后Orderby通過對該序列排序來對它進行
操作,因為查詢會回傳IEnumerable,所以您可通過將方法呼叫鏈接在一起,在方法語法中將這些查詢組合起來,這就是在您通過使用查詢語法撰寫查詢時編譯器在
后臺所執行的操作,并且由于查詢變數不存盤查詢的結果,因此您可以隨時修改它或將它用作新查詢的基礎,即使在執行它后,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/87824.html
標籤:C#
上一篇:Winform中怎樣設定ContextMenuStrip右鍵選單的選項ToolStripMenuItem添加照片
