LINQ 的标准查询操作符
生活随笔
收集整理的這篇文章主要介紹了
LINQ 的标准查询操作符
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
摘自msdn (http://msdn.microsoft.com/zh-cn/magazine/cc337893.aspx)
LINQ 的標準查詢操作符 John Papa
代碼下載位置: DataPoints2008_03.exe (958 KB)
Browse the Code Online 目錄 操作符和 LINQ
操作符類型
Lambda 表達式
First 和 Single
聚合、層次結(jié)構(gòu)和投影
投影和排序
限定符和轉(zhuǎn)換
結(jié)束語
語言集成查詢 (LINQ) 允許開發(fā)人員通過強類型化語法使用 Microsoft? .NET Framework 3.5 代碼編寫類似 SQL 的查詢。然后,各種 LINQ 提供程序,如 LINQ to Objects(可利用它根據(jù)對象層次結(jié)構(gòu)編寫查詢)和 LINQ to Entities(可利用它根據(jù)實體框架的概念模型編寫查詢)可根據(jù)代表數(shù)據(jù)存儲的細微差別來有效處理這些查詢。 除強類型化語法外,LINQ 查詢還具有一個標準查詢操作符庫來增強其功能。這些標準查詢操作符對序列進行運算并可執(zhí)行各種運算,如確定序列中是否存在某個值以及對序列運行合計函數(shù)(如求和)。 在本月的專欄中,我將使用 LINQ 來執(zhí)行實際的查詢和運算(會用到 LINQ to Objects 和 LINQ to Entities)。我將查詢一個實體集合并使用其導(dǎo)航屬性深入研究一組具備層次結(jié)構(gòu)的實體。我還會為您演示如何對數(shù)組和集合應(yīng)用多個標準查詢操作符。并展示如何使用 lambda 表達式強化 LINQ 的標準查詢操作符,以及如何利用它們來從序列解析特定信息并對序列執(zhí)行復(fù)雜的邏輯運算。本專欄的下載中提供有所有代碼示例(請參見 msdn.microsoft.com/msdnmag/code08.aspx)。
操作符和 LINQ LINQ 自身功能非常強大,無論使用的是 LINQ to XML、LINQ to DataSets、LINQ to Entities、LINQ to Objects 還是附帶的任何其他 LINQ 提供程序。LINQ 的核心功能在于其強類型化查詢語法,它可用于任意此類提供程序。當(dāng)將 LINQ 與一個或多個標準查詢操作符結(jié)合使用時,會得到一個功能更為強大的工具集,從而可精細地控制一組數(shù)據(jù)。 標準查詢操作符在 System.Linq 命名空間中的 System.Core.dll 程序集中作為靜態(tài)類 Enumerable 和 Queryable 的擴展方法存在,并且可用于實現(xiàn) IEnumerable<T> 或 IQueryable<T> 的對象。這樣它們就能使用 LINQ to Entities 和 LINQ to SQL 之類的提供程序?qū)Ω黝悓ο髨?zhí)行運算,從內(nèi)存中的集合和數(shù)組(序列)到遠程數(shù)據(jù)庫。 可輕松地確定處理特定任務(wù)時所擁有的操作符。如果要在 LINQ 查詢中使用操作符,可使用 Queryable 靜態(tài)類可用擴展方法中的操作符。如果要對實現(xiàn) IEnumerable<T> 的序列使用操作符,可使用 Enumerable 靜態(tài)類中的一個擴展方法。但是,請記住:并非 Queryable 類中的所有操作符都適用于基礎(chǔ)數(shù)據(jù)存儲,因此運行時可能不支持某些操作符。
操作符類型 操作符有多種類型(使用對象瀏覽器查看 Enumerable 和 Queryable 類即可找到所有操作符)。圖 A 以字母順序顯示了不同類型操作符的分類。可利用它來大致了解一下操作符所提供的功能。我將使用 LINQ to Objects 和 LINQ to Entities 展示一小組此類操作符,以顯示它們?nèi)绾螢閷嶋H應(yīng)用程序帶來好處。 Figure?A?Categories of Operators
Lambda 表達式 許多標準查詢操作符在對序列執(zhí)行運算時都使用 Func 委托來處理單個元素。Lambda 表達式可與標準查詢操作符結(jié)合使用以代表委托。lambda 表達式是創(chuàng)建委托實現(xiàn)的簡略表達形式,并可用于匿名委托適用的所有場合。C# 和 Visual Basic? .NET 均支持 Lambda 表達式。但是,必須注意:由于 Visual Basic .NET 尚不支持匿名方法,Lambda 表達式可能僅包含一個語句。 讓我們來看看如何對一個整數(shù)數(shù)組使用 Single 操作符。這個整數(shù)數(shù)組的每個元素代表 2 的 1 到 10 次方。先創(chuàng)建此數(shù)組,然后使用 Single 操作符來檢索滿足 Lambda 表達式中指定條件的單個整數(shù)元素: int[] nums = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; int singleNum = nums.Single(x => x > 16 && x < 64); Console.WriteLine(singleNum.ToString()); Lambda 表達式包含多個關(guān)鍵部分。Lambda 表達式首先定義傳入委托的變量。在以上代碼示例中,x(在 => 操作符左側(cè)聲明)是參數(shù),代表傳遞給它的 nums 數(shù)組中的每個元素。Lambda 表達式的剩余部分代表數(shù)組中每個元素的評估邏輯。可使用匿名委托輕松地重新編寫以上表達式,如下所示: ? int singleNum = nums.Single<int>( delegate(int x) {return (x > 16 && x < 64); } ) ; 但是,此代碼的可讀性不及 Lambda 表達式。C# 2.0 引入了可使委托的傳遞稍微輕松些的匿名委托;但是,Lambda 表達式的簡潔語法可使其更加簡單。
First 和 Single 如果必須從序列中提取一個值,First、FirstOrDefault、Single 和 SingleOrDefault 操作符都非常有用。First 方法返回序列中的第一個元素。First 有一個重載方法,可使用它來傳入 Lambda 表達式來代表一個條件。例如,如果要返回整數(shù)序列中整數(shù)元素大于 50 的第一個元素,可使用以下代碼示例: ? int[] nums = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; int num1 = nums.First<int>(); int num2 = nums.First<int>(x => x > 50); int num3 = nums.FirstOrDefault<int>(x => x > 5000); Console.WriteLine( num1.ToString() + "-" + num2.ToString() + "-" + num3.ToString()); 此代碼會查找第一個元素 (1)、大于 50 的第一個元素 (64) 以及大于 5,000 的第一個元素。由于數(shù)組中沒有元素滿足第三個 Lambda 表達式(數(shù)組中無整數(shù)大于 5,000),則如果代碼使用的是 First 操作符而非 FirstOrDefault,則會引發(fā)異常。在使用 FirstOrDefault 操作符時,如果沒有元素滿足 Lambda 表達式,則會返回 0。First 操作符也可用于 LINQ to Entities 查詢,如下所示: ? using (Entities entities = new Entities()) { var query = (from c in entities.Customers select c).First(c => c.City.Equals("London")); Console.WriteLine(query.CompanyName); } 在此示例中,將返回 London 城中的第一個客戶。正如您所看到的,當(dāng) First 方法用于各種 LINQ 提供程序(在本例中為 LINQ to Objects 和 LINQ to Entities)時,所用的語法并不會更改。 在 LINQ to Entities 上下文中,First 操作符非常有用,尤其是您知道會從查詢返回單個記錄時。例如,您可能有個查詢,它常在給出 CustomerID 時獲取一條客戶記錄。這種情況總是返回 0 或 1 條記錄,因此,得到一個序列不如就得到一個實體本身。換句話說,您寧愿獲取 Customer 實體而非 1 個 Customer 實體序列。First 方法在某種怦下非常有用,如以下代碼段所示。(由于實體框架不會嘗試在客戶端和服務(wù)器之間分發(fā)單個查詢的執(zhí)行,并且 LINQ to Entities 不支持 Single 方法,因此使用 First 方法是個輕松的替代方法。) ? using (Entities entities = new Entities()) { var query = (from c in entities.Customers where c.CustomerID.Equals("BOLID") select c).First(); Console.WriteLine(query.CompanyName); }
聚合、層次結(jié)構(gòu)和投影 在 LINQ to Entities 查詢中使用聚合操作符(如 Sum)可有助于簡化查詢。例如,以下代碼檢索訂單總額大于 $10,000 的一個訂單序列: ? using (Entities entities = new Entities()) { var query = from o in entities.Orders where o.OrderDetails.Sum( od => od.UnitPrice * od.Quantity) >= 10000 select o; foreach (Orders order in query) Console.WriteLine(order.OrderID); } 由于 LINQ 可查詢層次結(jié)構(gòu)實體集合,因此標準查詢操作符也可用于對嵌套實體序列執(zhí)行運算。當(dāng)必須計算或詢問派生數(shù)據(jù)時,這一點非常有用。派生數(shù)據(jù)可能僅存在于其基本窗體中,如客戶訂單的詳細信息僅包含單價和數(shù)量值。在本例中,未在模型中的任何位置提供代表訂單總金額的聚合數(shù)據(jù)。然而,通過在 LINQ 查詢中應(yīng)用 Sum 操作符,仍可檢索消費金額超過 $20,000 的所有客戶,如下所示: ? using (Entities entities = new Entities()) { var query = from c in entities.Customers where c.Orders.Sum( o => o.OrderDetails.Sum( od => od.UnitPrice * od.Quantity)) >= 25000 select c; foreach (Customers customer in query) Console.WriteLine(customer.CompanyName); } 此示例展示了如何在 LINQ 查詢的多個層次應(yīng)用標準查詢操作符。查詢最終會返回一個 Customers 實體序列,但為達到此目的,它必須首先深入每個客戶的訂單以及每個訂單的訂單詳細信息獲取所需數(shù)據(jù),這樣才可以計算每項的價格,匯總每個訂單的項目,然后匯總每個客戶的總額。 Count 操作符是另一聚合標準查詢操作符。可通過使用以下代碼確定有多少客戶的消費金額超過 $25,000: ? using (Entities entities = new Entities()) { var query = (from c in entities.Customers where c.Orders.Sum( o => o.OrderDetails.Sum( od => od.UnitPrice * od.Quantity)) >= 25000 select c).Count(); Console.WriteLine(query); } 可使用 Max 操作符來確定最佳客戶。以下代碼示例將返回消費最高的客戶所花費的金額。它在層次結(jié)構(gòu)的多個層級中組合使用 Sum 和 Max 聚合操作符: using (Entities entities = new Entities()) { var query = (from c in entities.Customers select new { c.CustomerID, Total = c.Orders.Sum( o => o.OrderDetails.Sum(od => od.UnitPrice)) }).Max(c2 => c2.Total); Console.WriteLine(query); }
投影和排序 您可能還注意到我在之前的示例中暗藏了一個投影。在使用 Max 操作符之前,LINQ 查詢并不返回客戶列表。而是會返回一個投影,此投影創(chuàng)建了包含 CustomerID 屬性和 Total 屬性(客戶的整個消費金額)的一個新實體。投影是 LINQ 必不可少的一部分,如前一示例所示,將它們投影到序列中后,就可使用標準查詢操作符來進一步處理它們。 圖 1 顯示了如何創(chuàng)建一個新實體投影,其中包含 CustomerID 和客戶的訂單總金額(使用之前討論的 Sum 操作符)。圖 1 還使用 OrderByDescending 操作符來按計算總額對投影實體序列進行排序。如果兩個客戶總額相同,還會使用另一排序操作符來進一步定義順序。例如,還可使用以下代碼修正圖 1 中的 foreach 語句以進一步限定排序規(guī)則: ?Figure?1?Aggregates, Projections, and Ordering 復(fù)制代碼 using (Entities entities = new Entities()) { var query = from c in entities.Customers where c.Orders.Sum( o => o.OrderDetails.Sum(od => od.UnitPrice)) > 0 select new { c.CustomerID, Total = c.Orders.Sum( o => o.OrderDetails.Sum(od => od.UnitPrice)) }; foreach (var item in query.OrderByDescending(x => x.Total)) Console.WriteLine(item.CustomerID + " == " + item.Total); } foreach (var item in query.OrderByDescending(x => x.Total) .ThenBy(x => x.CustomerID)) { Console.WriteLine(item.CustomerID + " == " + item.Total); } 在該代碼段中,我添加了 ThenBy 操作符和一個 Lambda 表達式,以表示序列應(yīng)首先按 Total 屬性降序排列,然后按投影的 CustomerID 屬性升序排列。
限定符和轉(zhuǎn)換 如果需要確定序列中是否存在某個值,可使用標準查詢操作符 Any。限定符(如 Any、All 和 Contains)會搜索元素序列,并評估序列是否滿足 lambda 表達式的條件。如果需檢查序列以確定某些事宜(例如:是否存在來自特定地址的客戶、所有客戶是否來自同一國家或者任意其他分析確定性問題),它將非常有用。 例如,以下 LINQ 查詢會檢查是否來自 United Kingdom 的所有客戶都位于 London。它使用限定符 All 并將其傳遞給僅評估城市是否為 London 的 lambda 表達式。如果序列中的每個元素都滿足此條件并且 lambda 表達式返回 true,然后 All 操作符會返回 true: using (Entities entities = new Entities()) { bool allUKCustomerAreFromLondon = (from c in entities.Customers where c.Country == "UK" select c).All( c => c.City.Equals("London")); Console.WriteLine(allUKCustomerAreFromLondon ? "Yes" : "No"); } 需在此查詢中詢問的另一問題是序列中是否有來自 United Kingdom 的 Cowes 的實體。對于此問題,可使用 Any 限定符來計算序列,如下所示: using (Entities entities = new Entities()) { bool isOneUKCustomerFromCowes = (from c in entities.Customers where c.Country == "UK" select c).Any( c => c.City.Equals("Cowes")); Console.WriteLine(isOneUKCustomerFromCowes? "Yes" : "No"); } Contains 操作符在評估序列中是否包括您所查找的項目時類似于 Any 操作符。Any 操作符可確定序列的某個項中是否存在某個值,而 Contains 操作符則確定序列中是否存在特定項目實例。例如,在將某個對象添加到序列中之前,您可能希望確保序列中并未包含該對象。圖 2 展示了如何檢查。 ?Figure?2?Using Contains and Conversion using (Entities entities = new Entities()) { Customers customerBSBEV = (from c in entities.Customers where c.CustomerID == "BSBEV" select c).First(); var customersUK = from c in entities.Customers where c.Country == "UK" select c; bool isCustomerInSequence = customersUK.Contains(customerBSBEV); Console.WriteLine(isCustomerInSequence? "Yes" : "No"); } 請注意:在圖 2 中,首先針對 BSBEV 客戶檢索 Customers 實體。然后,檢索客戶來自 United Kingdom 的 Customers 實體序列。最后,使用 Contains 操作符來檢查 Customers 序列是否包含 customerBSBEV 變量的實例。 圖 2 中所顯示的 Contains 操作符實現(xiàn)適用于可基于其實際實例信心十足地比較對象的場合。但是,如果需要 Contains 操作符根據(jù)邏輯標識進行測試又該如何呢?幸運的是,Contains 操作符包含一個重載,可使用它來傳遞實現(xiàn) IEqualityComparer<T> 接口的對象。要根據(jù) CustomerID 使用 Contains,可按如下所示重新編寫圖 2 中的代碼: using (Entities entities = new Entities()) { ... bool isCustomerInSequence = customersUK.Contains(customerBSBEV, new CustomerComparer()); Console.WriteLine(isCustomerInSequence? "Yes" : "No"); } 其中 CustomerComparer 定義為 ? private class CustomerComparer : IEqualityComparer<Customers> { public bool Equals(Customers x, Customers y) { if (x == null || y == null) return false; return x.CustomerID.Equals(y.CustomerID); } ... }
結(jié)束語 有許多標準查詢操作符均可定義為 Enumerable 和 Queryable 序列類的擴展方法。如我之前所示,這些操作符有助于擴展 LINQ 的功能。我還展示了結(jié)合使用多個 .NET Framework 3.5 新增強功能(包括 lambda 表達式、LINQ、實體框架和隱式類型化變量)來更加輕松地編寫功能強大的代碼和邏輯。
John Papa 是 ASPSOFT (aspsoft.com) 的一位資深 .NET 顧問,同時也是一位狂熱的棒球迷,在夏季的大多數(shù)夜晚,他都與家人以及忠實的狗 Kadi 一起為洋基隊加油。John 是 C# 領(lǐng)域的一位 MVP 和 INETA 發(fā)言人,撰寫過多本有關(guān)數(shù)據(jù)訪問技術(shù)方面的書籍。他經(jīng)常在行業(yè)會議上發(fā)表演講,或者在 http://www.johnpapa.net/ 上撰寫博客文章。
LINQ 的標準查詢操作符 John Papa
代碼下載位置: DataPoints2008_03.exe (958 KB)
Browse the Code Online 目錄 操作符和 LINQ
操作符類型
Lambda 表達式
First 和 Single
聚合、層次結(jié)構(gòu)和投影
投影和排序
限定符和轉(zhuǎn)換
結(jié)束語
語言集成查詢 (LINQ) 允許開發(fā)人員通過強類型化語法使用 Microsoft? .NET Framework 3.5 代碼編寫類似 SQL 的查詢。然后,各種 LINQ 提供程序,如 LINQ to Objects(可利用它根據(jù)對象層次結(jié)構(gòu)編寫查詢)和 LINQ to Entities(可利用它根據(jù)實體框架的概念模型編寫查詢)可根據(jù)代表數(shù)據(jù)存儲的細微差別來有效處理這些查詢。 除強類型化語法外,LINQ 查詢還具有一個標準查詢操作符庫來增強其功能。這些標準查詢操作符對序列進行運算并可執(zhí)行各種運算,如確定序列中是否存在某個值以及對序列運行合計函數(shù)(如求和)。 在本月的專欄中,我將使用 LINQ 來執(zhí)行實際的查詢和運算(會用到 LINQ to Objects 和 LINQ to Entities)。我將查詢一個實體集合并使用其導(dǎo)航屬性深入研究一組具備層次結(jié)構(gòu)的實體。我還會為您演示如何對數(shù)組和集合應(yīng)用多個標準查詢操作符。并展示如何使用 lambda 表達式強化 LINQ 的標準查詢操作符,以及如何利用它們來從序列解析特定信息并對序列執(zhí)行復(fù)雜的邏輯運算。本專欄的下載中提供有所有代碼示例(請參見 msdn.microsoft.com/msdnmag/code08.aspx)。
操作符和 LINQ LINQ 自身功能非常強大,無論使用的是 LINQ to XML、LINQ to DataSets、LINQ to Entities、LINQ to Objects 還是附帶的任何其他 LINQ 提供程序。LINQ 的核心功能在于其強類型化查詢語法,它可用于任意此類提供程序。當(dāng)將 LINQ 與一個或多個標準查詢操作符結(jié)合使用時,會得到一個功能更為強大的工具集,從而可精細地控制一組數(shù)據(jù)。 標準查詢操作符在 System.Linq 命名空間中的 System.Core.dll 程序集中作為靜態(tài)類 Enumerable 和 Queryable 的擴展方法存在,并且可用于實現(xiàn) IEnumerable<T> 或 IQueryable<T> 的對象。這樣它們就能使用 LINQ to Entities 和 LINQ to SQL 之類的提供程序?qū)Ω黝悓ο髨?zhí)行運算,從內(nèi)存中的集合和數(shù)組(序列)到遠程數(shù)據(jù)庫。 可輕松地確定處理特定任務(wù)時所擁有的操作符。如果要在 LINQ 查詢中使用操作符,可使用 Queryable 靜態(tài)類可用擴展方法中的操作符。如果要對實現(xiàn) IEnumerable<T> 的序列使用操作符,可使用 Enumerable 靜態(tài)類中的一個擴展方法。但是,請記住:并非 Queryable 類中的所有操作符都適用于基礎(chǔ)數(shù)據(jù)存儲,因此運行時可能不支持某些操作符。
操作符類型 操作符有多種類型(使用對象瀏覽器查看 Enumerable 和 Queryable 類即可找到所有操作符)。圖 A 以字母順序顯示了不同類型操作符的分類。可利用它來大致了解一下操作符所提供的功能。我將使用 LINQ to Objects 和 LINQ to Entities 展示一小組此類操作符,以顯示它們?nèi)绾螢閷嶋H應(yīng)用程序帶來好處。 Figure?A?Categories of Operators
| 聚合 | ? |
| Aggregate | 對序列執(zhí)行一個自定義方法 |
| Average | 計算數(shù)值序列的平均值 |
| Count | 返回序列中的項目數(shù)(整數(shù)) |
| LongCount | 返回序列中的項目數(shù)(長型) |
| Min | 查找數(shù)字序列中的最小數(shù) |
| Max | 查找數(shù)字序列中的最大數(shù) |
| Sum | 匯總序列中的數(shù)字 |
| 連接 | ? |
| Concat | 將兩個序列連成一個序列 |
| 轉(zhuǎn)換 | ? |
| Cast | 將序列中的元素轉(zhuǎn)換成指定類型 |
| OfType | 篩選序列中指定類型的元素 |
| ToArray | 從序列返回一個數(shù)組 |
| ToDictionary | 從序列返回一個字典 |
| ToList | 從序列返回一個列表 |
| ToLookup | 從序列返回一個查詢 |
| ToSequence | 返回一個 IEnumerable 序列 |
| 元素 | ? |
| DefaultIfEmpty | 為空序列創(chuàng)建默認元素 |
| ElementAt | 返回序列中指定索引的元素 |
| ElementAtOrDefault | 返回序列中指定索引的元素,或者如果索引超出范圍,則返回默認值 |
| First | 返回序列中的第一個元素 |
| FirstOrDefault | 返回序列中的第一個元素,或者如果未找到元素,則返回默認值 |
| Last | 返回序列中的最后一個元素 |
| LastOrDefault | 返回序列中的最后一個元素,或者如果未找到元素,則返回默認值 |
| Single | 返回序列中的單個元素 |
| SingleOrDefault | 返回序列中的單個元素,或者如果未找到元素,則返回默認值 |
| 相等 | ? |
| SequenceEqual | 比較兩個序列看其是否相等 |
| 生成 | ? |
| Empty | 生成一個空序列 |
| Range | 生成一個指定范圍的序列 |
| Repeat | 通過將某個項目重復(fù)指定次數(shù)來生成一個序列 |
| 分組 | ? |
| GroupBy | 按指定分組方法對序列中的項目進行分組 |
| 聯(lián)接 | ? |
| GroupJoin | 通過歸組將兩個序列聯(lián)接在一起 |
| Join | 將兩個序列從內(nèi)部聯(lián)接起來 |
| 排序 | ? |
| OrderBy | 以升序按值排列序列 |
| OrderByDescending | 以降序按值排列序列 |
| ThenBy | 升序排列已排序的序列 |
| ThenByDescending | 降序排列已排序的序列 |
| Reverse | 顛倒序列中項目的順序 |
| 分區(qū) | ? |
| Skip | 返回跳過指定數(shù)目項目的序列 |
| SkipWhile | 返回跳過不滿足表達式項目的序列 |
| Take | 返回具有指定數(shù)目項目的序列 |
| TakeWhile | 返回具有滿足表達式項目的序列 |
| 投影 | ? |
| Select | 創(chuàng)建部分序列的投影 |
| SelectMany | 創(chuàng)建部分序列的一對多投影 |
| 限定符 | ? |
| All | 確定序列中的所有項目是否滿足某個條件 |
| Any | 確定序列中是否有任何項目滿足條件 |
| Contains | 確定序列是否包含指定項目 |
| 限制 | ? |
| Where | 篩選序列中的項目 |
| 設(shè)置 | ? |
| Distinct | 返回?zé)o重復(fù)項目的序列 |
| Except | 返回代表兩個序列差集的序列 |
| Intersect | 返回代表兩個序列交集的序列 |
| Union | 返回代表兩個序列交集的序列 |
Lambda 表達式 許多標準查詢操作符在對序列執(zhí)行運算時都使用 Func 委托來處理單個元素。Lambda 表達式可與標準查詢操作符結(jié)合使用以代表委托。lambda 表達式是創(chuàng)建委托實現(xiàn)的簡略表達形式,并可用于匿名委托適用的所有場合。C# 和 Visual Basic? .NET 均支持 Lambda 表達式。但是,必須注意:由于 Visual Basic .NET 尚不支持匿名方法,Lambda 表達式可能僅包含一個語句。 讓我們來看看如何對一個整數(shù)數(shù)組使用 Single 操作符。這個整數(shù)數(shù)組的每個元素代表 2 的 1 到 10 次方。先創(chuàng)建此數(shù)組,然后使用 Single 操作符來檢索滿足 Lambda 表達式中指定條件的單個整數(shù)元素: int[] nums = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; int singleNum = nums.Single(x => x > 16 && x < 64); Console.WriteLine(singleNum.ToString()); Lambda 表達式包含多個關(guān)鍵部分。Lambda 表達式首先定義傳入委托的變量。在以上代碼示例中,x(在 => 操作符左側(cè)聲明)是參數(shù),代表傳遞給它的 nums 數(shù)組中的每個元素。Lambda 表達式的剩余部分代表數(shù)組中每個元素的評估邏輯。可使用匿名委托輕松地重新編寫以上表達式,如下所示: ? int singleNum = nums.Single<int>( delegate(int x) {return (x > 16 && x < 64); } ) ; 但是,此代碼的可讀性不及 Lambda 表達式。C# 2.0 引入了可使委托的傳遞稍微輕松些的匿名委托;但是,Lambda 表達式的簡潔語法可使其更加簡單。
First 和 Single 如果必須從序列中提取一個值,First、FirstOrDefault、Single 和 SingleOrDefault 操作符都非常有用。First 方法返回序列中的第一個元素。First 有一個重載方法,可使用它來傳入 Lambda 表達式來代表一個條件。例如,如果要返回整數(shù)序列中整數(shù)元素大于 50 的第一個元素,可使用以下代碼示例: ? int[] nums = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; int num1 = nums.First<int>(); int num2 = nums.First<int>(x => x > 50); int num3 = nums.FirstOrDefault<int>(x => x > 5000); Console.WriteLine( num1.ToString() + "-" + num2.ToString() + "-" + num3.ToString()); 此代碼會查找第一個元素 (1)、大于 50 的第一個元素 (64) 以及大于 5,000 的第一個元素。由于數(shù)組中沒有元素滿足第三個 Lambda 表達式(數(shù)組中無整數(shù)大于 5,000),則如果代碼使用的是 First 操作符而非 FirstOrDefault,則會引發(fā)異常。在使用 FirstOrDefault 操作符時,如果沒有元素滿足 Lambda 表達式,則會返回 0。First 操作符也可用于 LINQ to Entities 查詢,如下所示: ? using (Entities entities = new Entities()) { var query = (from c in entities.Customers select c).First(c => c.City.Equals("London")); Console.WriteLine(query.CompanyName); } 在此示例中,將返回 London 城中的第一個客戶。正如您所看到的,當(dāng) First 方法用于各種 LINQ 提供程序(在本例中為 LINQ to Objects 和 LINQ to Entities)時,所用的語法并不會更改。 在 LINQ to Entities 上下文中,First 操作符非常有用,尤其是您知道會從查詢返回單個記錄時。例如,您可能有個查詢,它常在給出 CustomerID 時獲取一條客戶記錄。這種情況總是返回 0 或 1 條記錄,因此,得到一個序列不如就得到一個實體本身。換句話說,您寧愿獲取 Customer 實體而非 1 個 Customer 實體序列。First 方法在某種怦下非常有用,如以下代碼段所示。(由于實體框架不會嘗試在客戶端和服務(wù)器之間分發(fā)單個查詢的執(zhí)行,并且 LINQ to Entities 不支持 Single 方法,因此使用 First 方法是個輕松的替代方法。) ? using (Entities entities = new Entities()) { var query = (from c in entities.Customers where c.CustomerID.Equals("BOLID") select c).First(); Console.WriteLine(query.CompanyName); }
聚合、層次結(jié)構(gòu)和投影 在 LINQ to Entities 查詢中使用聚合操作符(如 Sum)可有助于簡化查詢。例如,以下代碼檢索訂單總額大于 $10,000 的一個訂單序列: ? using (Entities entities = new Entities()) { var query = from o in entities.Orders where o.OrderDetails.Sum( od => od.UnitPrice * od.Quantity) >= 10000 select o; foreach (Orders order in query) Console.WriteLine(order.OrderID); } 由于 LINQ 可查詢層次結(jié)構(gòu)實體集合,因此標準查詢操作符也可用于對嵌套實體序列執(zhí)行運算。當(dāng)必須計算或詢問派生數(shù)據(jù)時,這一點非常有用。派生數(shù)據(jù)可能僅存在于其基本窗體中,如客戶訂單的詳細信息僅包含單價和數(shù)量值。在本例中,未在模型中的任何位置提供代表訂單總金額的聚合數(shù)據(jù)。然而,通過在 LINQ 查詢中應(yīng)用 Sum 操作符,仍可檢索消費金額超過 $20,000 的所有客戶,如下所示: ? using (Entities entities = new Entities()) { var query = from c in entities.Customers where c.Orders.Sum( o => o.OrderDetails.Sum( od => od.UnitPrice * od.Quantity)) >= 25000 select c; foreach (Customers customer in query) Console.WriteLine(customer.CompanyName); } 此示例展示了如何在 LINQ 查詢的多個層次應(yīng)用標準查詢操作符。查詢最終會返回一個 Customers 實體序列,但為達到此目的,它必須首先深入每個客戶的訂單以及每個訂單的訂單詳細信息獲取所需數(shù)據(jù),這樣才可以計算每項的價格,匯總每個訂單的項目,然后匯總每個客戶的總額。 Count 操作符是另一聚合標準查詢操作符。可通過使用以下代碼確定有多少客戶的消費金額超過 $25,000: ? using (Entities entities = new Entities()) { var query = (from c in entities.Customers where c.Orders.Sum( o => o.OrderDetails.Sum( od => od.UnitPrice * od.Quantity)) >= 25000 select c).Count(); Console.WriteLine(query); } 可使用 Max 操作符來確定最佳客戶。以下代碼示例將返回消費最高的客戶所花費的金額。它在層次結(jié)構(gòu)的多個層級中組合使用 Sum 和 Max 聚合操作符: using (Entities entities = new Entities()) { var query = (from c in entities.Customers select new { c.CustomerID, Total = c.Orders.Sum( o => o.OrderDetails.Sum(od => od.UnitPrice)) }).Max(c2 => c2.Total); Console.WriteLine(query); }
投影和排序 您可能還注意到我在之前的示例中暗藏了一個投影。在使用 Max 操作符之前,LINQ 查詢并不返回客戶列表。而是會返回一個投影,此投影創(chuàng)建了包含 CustomerID 屬性和 Total 屬性(客戶的整個消費金額)的一個新實體。投影是 LINQ 必不可少的一部分,如前一示例所示,將它們投影到序列中后,就可使用標準查詢操作符來進一步處理它們。 圖 1 顯示了如何創(chuàng)建一個新實體投影,其中包含 CustomerID 和客戶的訂單總金額(使用之前討論的 Sum 操作符)。圖 1 還使用 OrderByDescending 操作符來按計算總額對投影實體序列進行排序。如果兩個客戶總額相同,還會使用另一排序操作符來進一步定義順序。例如,還可使用以下代碼修正圖 1 中的 foreach 語句以進一步限定排序規(guī)則: ?Figure?1?Aggregates, Projections, and Ordering 復(fù)制代碼 using (Entities entities = new Entities()) { var query = from c in entities.Customers where c.Orders.Sum( o => o.OrderDetails.Sum(od => od.UnitPrice)) > 0 select new { c.CustomerID, Total = c.Orders.Sum( o => o.OrderDetails.Sum(od => od.UnitPrice)) }; foreach (var item in query.OrderByDescending(x => x.Total)) Console.WriteLine(item.CustomerID + " == " + item.Total); } foreach (var item in query.OrderByDescending(x => x.Total) .ThenBy(x => x.CustomerID)) { Console.WriteLine(item.CustomerID + " == " + item.Total); } 在該代碼段中,我添加了 ThenBy 操作符和一個 Lambda 表達式,以表示序列應(yīng)首先按 Total 屬性降序排列,然后按投影的 CustomerID 屬性升序排列。
限定符和轉(zhuǎn)換 如果需要確定序列中是否存在某個值,可使用標準查詢操作符 Any。限定符(如 Any、All 和 Contains)會搜索元素序列,并評估序列是否滿足 lambda 表達式的條件。如果需檢查序列以確定某些事宜(例如:是否存在來自特定地址的客戶、所有客戶是否來自同一國家或者任意其他分析確定性問題),它將非常有用。 例如,以下 LINQ 查詢會檢查是否來自 United Kingdom 的所有客戶都位于 London。它使用限定符 All 并將其傳遞給僅評估城市是否為 London 的 lambda 表達式。如果序列中的每個元素都滿足此條件并且 lambda 表達式返回 true,然后 All 操作符會返回 true: using (Entities entities = new Entities()) { bool allUKCustomerAreFromLondon = (from c in entities.Customers where c.Country == "UK" select c).All( c => c.City.Equals("London")); Console.WriteLine(allUKCustomerAreFromLondon ? "Yes" : "No"); } 需在此查詢中詢問的另一問題是序列中是否有來自 United Kingdom 的 Cowes 的實體。對于此問題,可使用 Any 限定符來計算序列,如下所示: using (Entities entities = new Entities()) { bool isOneUKCustomerFromCowes = (from c in entities.Customers where c.Country == "UK" select c).Any( c => c.City.Equals("Cowes")); Console.WriteLine(isOneUKCustomerFromCowes? "Yes" : "No"); } Contains 操作符在評估序列中是否包括您所查找的項目時類似于 Any 操作符。Any 操作符可確定序列的某個項中是否存在某個值,而 Contains 操作符則確定序列中是否存在特定項目實例。例如,在將某個對象添加到序列中之前,您可能希望確保序列中并未包含該對象。圖 2 展示了如何檢查。 ?Figure?2?Using Contains and Conversion using (Entities entities = new Entities()) { Customers customerBSBEV = (from c in entities.Customers where c.CustomerID == "BSBEV" select c).First(); var customersUK = from c in entities.Customers where c.Country == "UK" select c; bool isCustomerInSequence = customersUK.Contains(customerBSBEV); Console.WriteLine(isCustomerInSequence? "Yes" : "No"); } 請注意:在圖 2 中,首先針對 BSBEV 客戶檢索 Customers 實體。然后,檢索客戶來自 United Kingdom 的 Customers 實體序列。最后,使用 Contains 操作符來檢查 Customers 序列是否包含 customerBSBEV 變量的實例。 圖 2 中所顯示的 Contains 操作符實現(xiàn)適用于可基于其實際實例信心十足地比較對象的場合。但是,如果需要 Contains 操作符根據(jù)邏輯標識進行測試又該如何呢?幸運的是,Contains 操作符包含一個重載,可使用它來傳遞實現(xiàn) IEqualityComparer<T> 接口的對象。要根據(jù) CustomerID 使用 Contains,可按如下所示重新編寫圖 2 中的代碼: using (Entities entities = new Entities()) { ... bool isCustomerInSequence = customersUK.Contains(customerBSBEV, new CustomerComparer()); Console.WriteLine(isCustomerInSequence? "Yes" : "No"); } 其中 CustomerComparer 定義為 ? private class CustomerComparer : IEqualityComparer<Customers> { public bool Equals(Customers x, Customers y) { if (x == null || y == null) return false; return x.CustomerID.Equals(y.CustomerID); } ... }
結(jié)束語 有許多標準查詢操作符均可定義為 Enumerable 和 Queryable 序列類的擴展方法。如我之前所示,這些操作符有助于擴展 LINQ 的功能。我還展示了結(jié)合使用多個 .NET Framework 3.5 新增強功能(包括 lambda 表達式、LINQ、實體框架和隱式類型化變量)來更加輕松地編寫功能強大的代碼和邏輯。
請將您想向 John 詢問的問題和提出的意見發(fā)送至?mmdatat@microsoft.com.
John Papa 是 ASPSOFT (aspsoft.com) 的一位資深 .NET 顧問,同時也是一位狂熱的棒球迷,在夏季的大多數(shù)夜晚,他都與家人以及忠實的狗 Kadi 一起為洋基隊加油。John 是 C# 領(lǐng)域的一位 MVP 和 INETA 發(fā)言人,撰寫過多本有關(guān)數(shù)據(jù)訪問技術(shù)方面的書籍。他經(jīng)常在行業(yè)會議上發(fā)表演講,或者在 http://www.johnpapa.net/ 上撰寫博客文章。
轉(zhuǎn)載于:https://www.cnblogs.com/lxydmq/archive/2008/05/29/1209834.html
總結(jié)
以上是生活随笔為你收集整理的LINQ 的标准查询操作符的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Eclipse环境下面的配置(转载)
- 下一篇: 解决MySQL数据库中文模糊检索问题