狠狠色丁香婷婷综合尤物/久久精品综合一区二区三区/中国有色金属学报/国产日韩欧美在线观看 - 国产一区二区三区四区五区tv

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

C#.Net筑基-優雅LINQ的查詢藝術

freeflydom
2025年7月2日 9:59 本文熱度 486

?Linq(Language Integrated Query,集成查詢語言),顧名思義就是用來查詢數據的一種語言(可以看作是一組功能、框架特性的集合)。在.NETFramework3.5(大概2007年)引入C#,用統一的C#語言快速查詢各種數據,如數據庫、XML文檔、對象集合等等。Linq的誕生對 C# 編程領域產生了深遠而持久的影響,改變了開發人員對查詢的思考方式。

  • 使用簡單:統一語法(鏈式方法語法、類似SQL的查詢語法),智能提示。
  • 類型安全:編譯時強類型檢查,減少運行時錯誤。
  • 延遲執行,查詢本身只是構建了一個表達式,在真正使用的時候(foreach、ToList、查詢數據庫)才會執行。
  • 支持多種數據源:內存中的集合,以及各種外部數據庫。

Linq支持查詢任何實現了IEnumerable<T>接口的集合類型,基本上所有集合數據都支持Linq查詢。如下示例:大于 5 的偶數,并倒敘排列取前三名

//方法鏈語法
var query = arr.Where(n => n > 5 && n % 2 == 0).OrderByDescending(n => n).Take(3);

01、Linq 基礎概括

1.1、Linq語法:鏈式方法、查詢表達式

Linq 有兩種語法風格,如下實例代碼,一種是常規C#方法調用方式,另外一種是類似SQL的查詢表達式。這兩種語法其本質是一樣的,編譯后的中間語言(IL)是一樣的,確實僅僅只是語法形式不同而已

??鏈式方法:就是字面意思,函數式方法調用。這些方法都來自 IEnumerable 接口或 IQueryable 接口的擴展方法,這些方法提供了過濾、聚合、排序等多種查詢功能。

??查詢表達式:查詢表達式由一組用類似于 SQL 的聲明性語法所編寫的子句組成。 每個子句依次包含一個或多個 C# 表達式,而這些表達式可能本身就是查詢表達式,或者包含查詢表達式。查詢表達式必須以 from 子句開頭,且必須以 select 或 group 子句結尾。

//方法鏈語法
var query = arr.Where(n => n > 5 && n % 2 == 0).OrderByDescending(n => n).Take(3);
//查詢表達式語法,類似數據庫SQL語言+C#的語法風格
var query2 = (from n in arr
where n > 5 && n % 2 == 0
                 orderby n descending
                 select n).Take(3);
比較鏈式方法查詢表達式(SQL)
特點鏈式方法調用,函數式編程類似SQL語句,自然語言,容易掌握
語法形式點點點鏈式方法調用,Where().Select().Order()from開頭:from...where...select
常用方法/語法System.Linq 上提供的擴展方法或第三方擴展:Where、OrderBy、Select、Skip、Take、Union僅支持編譯器識別的關鍵字:from、where、orderby、group、join、let、select、into、in、on等
本質System.Linq 提供的擴展方法調用編譯為標準查詢運算符方法調用,編譯結果和鏈式方法一樣
功能完整性完整的Linq功能有些能力沒有對應語法(如Max),需要結合鏈式方法使用

?? 兩種編寫方式編譯后生成的IL代碼實際上是一樣的,也可以混合使用,因此他們并沒有性能差異。

查詢表達式并不能實現獲取前3個元素,此時就需要兩者混合使用,

var query = from u in list
   where u.Age>14
   group u by u.Address into gu
   orderby gu.Count() descending
   select (gu.Key,gu.Count());
query = query.Take(3);

1.2、Linq執行:本地查詢、解釋型查詢

LINQ 提供了兩種用途的架構:針對本地(內存)對象的本地查詢,以及針對遠程數據源(數據庫)的解釋性查詢。兩者的語法形式基本一樣,都支持鏈式方法、查詢表達式。

??本地查詢:實現了針對IEnumerable的內存集合(數組、List)的查詢,其Linq的擴展方法都在 System.Linq.Enumerable 類中。查詢只是構建了一個可枚舉的迭代裝飾器序列,延遲在使用(消費)數據時執行。

??解釋查詢:解釋查詢是描述性的,實現了針對IQueryable(Table、DbSet)的遠程數據查詢,對應擴展方法都在 System.Linq.Queryable 類中。他們在運行時生成表達式樹,并進行解釋為SQL語句,在數據庫中執行該SQL語句并獲取數據。

比較本地查詢 Enumerable解釋查詢 Queryable
操作對象內存中的集合(IEnumerable<T>外部數據源的查詢接口(IQueryable<T>
延遲執行支持,真正使用(消費)數據時才執行,如 foreach、ToList支持,消費數據時才翻譯成SQL并在數據庫中執行獲取數據
執行原理參數為委托方法,C#內部執行委托、迭代器參數為表達式樹,LINQ Provider 在運行時遍歷該樹轉換為目標語言(如 SQL)
誰來執行CLR本地執行,數據在內存中數據庫執行SQL,數據在數據庫中
執行過程本地逐個元素迭代調用委托數據庫中執行SQL,返回查詢結果
使用場景List、Array、普通內存數據Entity Framework、LINQ to SQL、MongoDB 查詢
語法都支持鏈式方法、表達式查詢同樣支持鏈式方法、表達式查詢
Linq方法在哪里?System.Linq.Enumerable 靜態類System.Linq.Queryable 類,方法和 Enumerable 大部分對應。有些方法并不能生成數據庫兼容的SQL語法。
擴展性內存查詢支持任意C#方法,擴展性強受限,只能使用數據庫兼容的方法。如正則表達式SQLServer就不支持。
結合使用本地數據只能用本地查詢遠程數據可以結合本地查詢混用。

IQueryable 繼承自 IEnumerable,因此解釋查詢可以轉換為本地查詢,query.AsEnumerable(),不過需謹慎使用,會將數據庫的相應數據都加載到內存中。

public interface IQueryable<out T> : IEnumerable<T>, IEnumerable, IQueryable
{
}

??表達式樹是一個微型的代碼DOM結構,樹中的節點是Expression類型的節點,涵蓋各種語法形式,如參數、變量、常量、賦值、比較、循環等等。表達式樹可以轉換(Compile)為委托,反之則不能。

1.3、延遲執行的注意事項

延遲執行是指查詢代碼不會立刻執行,而是在正真取數的時候才會執行。他是Linq最主要的特點,是優點,也不全是,有些需要注意的地方。

  • 并不是所有的Linq方法都是延遲的,如:First()、Last()、ToArray()、ToList(),及Count、Max等聚合計算方法會立即執行。
  • 如果數據源變了,結果也會變化。
List<int> list = [2,3,9,4,5];
var query = list.Where(s=>s>5);
Console.WriteLine(query.Sum()); //9
list.Add(6);
Console.WriteLine(query.Sum()); //15
  • 重復取數時,查詢也會重復執行,可能會浪費性能,特別是復雜、耗時的查詢。避免的方式就是query.ToList() 一次性立即獲取數據。
  • Lambda變量捕獲,變量的值在真正執行查詢的時候才會獲取,這是方法閉包的特點。
List<int> list = [2,3,9,4,5];
int n = 5;
var query = list.Where(s=>s>n);
n = 4;
Console.WriteLine(query.Sum()); //14 //使用的n=4

1.4、迭代裝飾器序列

為了支持延遲執行,Linq內部封裝了很多迭代裝飾器,偷偷看了下源碼,如 WhereIteratorSelectEnumerableIteratorReverseIteratorUnionIterator 等,都是Linq內部的迭代裝飾器。迭代裝飾器會保留輸入序列的引用及其他相關參數,僅當枚舉結果時才會執行。

迭代序列裝飾器本身繼承自IEnumerable,因此就支持裝飾器之間的嵌套。下面為迭代裝飾器序列基類的源碼 Iterator.cs

internal abstract class Iterator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>
{
   private readonly int _threadId;
   internal int _state;
   internal TSource _current = default!;
}

2、Enumerable擴展方法匯總?

內存集合的Linq擴展方法,基本都來自Enumerable類,參考官方 Enumerable 類。用于數據庫的解釋性查詢方法在 System.Linq.Queryable 類中,方法和 Enumerable 基本上都是對應的。基本上所有的Linq方法都在這里匯總:

方法說明
Chunk(Int32)分塊拆分為多個固定大小的數組,返回IEnumerable<TSource[]>,內部每次迭代會構建一個數組new TSource[arraySize]
Append(T)末尾追加一個元素,原理是內部構建了一個新的迭代器AppendPrepend1Iterator實現返回這個元素。
Prepend(T)在前面追加一個元素,原理同上,是同一個AppendPrepend1Iterator
??聚合計算,立即執行
Count()獲取集合中元素的數量,可指定條件參數Func。arr.Count(),內部原理比較簡單,如果集合是ICollection等,則直接獲取Count,否則只能e.MoveNext()一個一個的數了。
TryGetNonEnumeratedCount獲取元素數量,在不真正遍歷(不枚舉)集合的情況下,盡量嘗試快速拿到集合元素的數量
Max()返回最大的那個元素。截止.NET8,整數類型用了Vector提升性,其他循環比較,性能一般??。
Min()返回最小的那個元素,性能原理同Max
Average()計算平均值,對于數值類型,內部用到了Vector?,性能還是不錯的。var a = arr.Average()
Sum()求和,arr1.Sum()
Aggregate(Func)執行累加器函數,函數的的輸出為作為下一輪迭代的輸入,依次迭代執行。
示例,計算序列最大值:var max = arr.Aggregate((acc,n)=>acc>n?acc:n)
??條件判斷
Contains(T)判斷是否包含指定元素,返回bool,可指定比較器。bool f = arr.Contains(6)
Any()集合是否包含元素,判斷集合是否不為空。if(arr.Any()){}
Any(Func)集合是否包含指定條件的元素,示例:是否有人考試滿分,bool flag = arr.All(n=>n==100)
All(Func)所有元素是否滿足條件,示例:是否所有同學都及格了,bool flag = arr.All(n=>n>=60)
SequenceEqual(IEnumerable)序列相等比較,比較兩個序列是否相同,長度相同、每個元素相等則返回True
??元素選擇
First()返回第一個元素,如果一個都沒有拋出異常,arr1.First()
FirstOrDefault()返回第一元素,如果一個都沒有則返回默認值,arr1.FirstOrDefault()
Last()返回最后一個元素,如果一個都沒有拋出異常。如果不是常規集合,會foreach循環所有??。
LastOrDefault()同上,如果一個都木有則返回默認值
Single()SingleOrDefault()獲取唯一元素,如果元素數量大于1則拋出異常。這個方法在數據庫按主鍵查詢時比較有用。
ElementAt(Index)返回指定索引Index位置的元素,arr.ElementAt(0)。還有個更安全的 ElementAtOrDefault
DefaultIfEmpty(defaultT)如果集合為空(集合中沒有元素)返回含一個默認值的IEnumerable,否則返回原序列。
??篩選查詢
Where(Func)條件查詢,最常用的Linq函數了,arr1.Where(s=>s>5)
Select(selector)返回指定Key(元素選擇處理器結果)的集合,list.Select(s=>s.Name+s.Age)
SelectMany()將每個元素的“內部集合”展開合并為一個大集合,list.SelectMany(s=>s.Name.Split('-'))
Distinct()去重,arr.Distinct(),內部使用HashSet<TSource>來去重。DistinctBy>可指定鍵Key。
OfType()根據類型T篩選集合,源碼中用obj is TResult來篩選,不符合的丟棄。list.OfType<double>()
Skip(int count)跳過指定數量的元素,返回剩余的元素,arr1.Skip(5)
SkipLast(int count)忽略后面的元素,返回前面剩余的元素。arr1.SkipLast(3)
SkipWhile(Func)從開頭跳過符合條件的元素,直到遇到不符合條件時停下,返回剩下的元素。
Take(int count)返回前n個元素,Skip的逆運算,Take(3)
TakeLast(int count)返回最后n個元素,arr1.TakeLast(3)
TakeWhile(Func)從開頭返回符合條件的元素,直到遇到不符合條件時停下,與SkipWhile相反arr1.TakeWhile(s=>s<5)
??排序分組
Order()升序排列集合,arr2.Order()
OrderBy(TKey)指定Key鍵升序排列集合,list.OrderBy(s=>s.Age)
OrderByDescending(TKey)指定Key鍵降序排列集合,list.OrderByDescending(s=>s.Age)
ThenByThenByDescending二次排序,跟著OrderBy使用,設置第二排序鍵。list.OrderBy(s=>s.Grade).ThenBy(s=>s.Age)
Reverse()反轉序列中元素的順序,arr2.Reverse()。內部源碼是創建了一個數組來實現翻轉,性能不佳??,數組推薦使用Array.Reverse(),原地翻轉,不會創建額外對象。
GroupBy按指定的Key分組,返回一個分組集合IGrouping<TKey, TSource>list.GroupBy(s=>s.Name)
GroupJoin帶分組的連接(Join)操作,類似Sql中的Left Join + 分組,每個「左邊元素」對應到「右邊的一組元素」
??多集合操作
Union(IEnumerable)并集,合并兩個集合并去重arr1.Union(arr2)
Intersect(IEnumerable)交集(Intersect /??nt??sekt/ 相交),返回兩個集合都包含的元素。IntersectBy 可指定鍵Key。
Except(IEnumerable)移除(Except /?k?sept/ 除外)arr1.Except(arr2)移除arr2中也存在的元素。ExceptBy可指定鍵Key。
Concat(IEnumerable)“合并”兩個序列集合(),內部由私有的ConcatIterator實現的連接迭代,arr.Concat([3])
Join(arr2, k2,k1,Func)兩個“表”內連接,類似Sql中的 Inner Join,用于兩個不同類型元素的的連接,兩個表Key匹配的元素合并
Zip就像拉鏈(zipper)一樣,把兩個序列一對一地配對合并成一個新序列,arr1.Zip(arr2,(n1,n2)=>n1+n2)
??轉換,ToXX立即執行?謹慎使用,會創建新的集合對象
Cast()強制類型轉換,內部使用強制轉換“(TResult)obj
ToArray()從 IEnumerable 創建新數組,慎用。var narr = arr1.Order().ToArray()
ToList()從 IEnumerable 創建新Listarr1.Take(3).ToList()
ToHashSet從 IEnumerable 創建新HashSet(不可重復集合,自動去重),arr1.ToHashSet()
ToDictionary()從 IEnumerable 創建新字典Dictionary<TK,TV>list.ToDictionary(s=>s.Name,s=>s.Age)
ToLookup()從 IEnumerable 創建新 Lookup分組的字典),arr1.ToLookup(s=>s%2)
??其他
Range(start, end)靜態方法,創建一個連續的序列,可用來創建測試數據,Enumerable.Range(1,10).ToArray()
Repeat(T, count)靜態方法,創建一個重復值的序列,Enumerable.Repeat(18,10)
Empty()靜態方法,獲得一個空的序列,Enumerable.Empty<int>().Any(); //false
AsEnumerable()返回自己,什么也不干。在Linq to SQL中可以強制讓后續操作在本地內存中進行,而不會翻譯成SQL。


2.1、示例:構建動態查詢

根據用戶輸入條件,構建動態查詢條件,使用 Skip 和 Take 實現分頁

var query = list.AsEnumerable();
if (!string.IsNullOrWhiteSpace(name))
   query = query.Where(s => s.Name.Contains(name, StringComparison.OrdinalIgnoreCase));
if (age.HasValue)
   query = query.Where(s => s.Age == age);
if (!string.IsNullOrWhiteSpace(address))
   query = query.Where(s => s.Address.Contains(address));
//使用 Skip 和 Take 實現分頁
query = query
   .Skip((pageNumber - 1) * pageSize)
   .Take(pageSize);
//執行查詢,獲取結果
var result = query.ToArray();

2.2、自定義擴展

本地查詢擴展是很容易的,基于IEnumerable<T>實現擴展方法即可。IQueryable擴展則要考慮數據庫的支持和映射,一般無需自定義擴展。

//交替獲取元素集合
public static IEnumerable<T> AlternateElements<T>(this IEnumerable<T> source)
{
   int index = 0;
   foreach (T element in source)
   {
       if (index % 2 == 0)
       {
           yield return element;
       }

       index++;
   }
}

//使用
var query = list.AlternateElements();

??版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請注明出處!原文編輯地址-語雀


該文章在 2025/7/2 9:59:31 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved