本筆記摘抄自:https://www.cnblogs.com/liqingwen/p/5832322.html,記錄一下學習過程以備後續查用。 LINQ 簡介: 語言集成查詢(LINQ)是Visual Studio 2008和.NET Framework 3.5版中引入的一項創新功能。 傳統上,針對 ...
本筆記摘抄自: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
運行結果如下:
1.3查詢查詢指定要從數據源中檢索的信息,可以指定在返回這些信息之前如何對其進行排序、分組和結構化。 查詢存儲在查詢變數中,並用查詢表達式進行初始
化。之前的示例中的查詢是從整數數組中返回所有的偶數。該查詢表達式包含三個子句: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 ); //執行查詢