LINQ 查詢
var query = from r in Formula1.GetChampions() where r.Country == "Brazil" orderby r.Wins descending select r; foreach (Racer racer in query) { Console.WriteLine("{0:A}", racer); }
擴展方法
LINQ為IEnumerable<T>接口提供各種擴展方法,以便用戶實現了該接口的任意集合上使用LINQ查詢。擴展方法在靜態類中聲明,定義一個靜態方法,第一參數定義擴展的類型。
擴展方法可以將方法寫入最初沒有提供該方法的類中,可以把方法添加到實現某個特定接口的任何類中,這樣多個類可以使用相同的實現代碼。
class Program { private static void Main(string[] args) { string s1 = "111111"; s1.Function(); string s2 = "2222222"; s2.Function(); } } public static class StringExtension { public static void Function(this string s) { Console.WriteLine("this string is " + s); } }
第一個參數 this 用來區分是擴展方法還是靜態方法。
第二個參數 需要對應擴展的類。
注意 擴展方法里不能訪問類型的私有成員。
還可以這樣調用
StringExtension.Function(s1);
LINQ 擴展示例
var champions = new List<Racer>(Formula1.GetChampions()); IEnumerable<Racer> brazilChampions = champions.Where(r => r.Country == "Brazil"). OrderByDescending(r => r.Wins). Select(r => r); foreach (Racer r in brazilChampions) { Console.WriteLine("{0:A}", r); }
推遲查詢的執行
var names = new List<string> { "Nino", "Alberto", "Juan", "Mike", "Phil" }; var namesWithJ = from n in names where n.StartsWith("J") orderby n select n; Console.WriteLine("First iteration"); foreach (string name in namesWithJ) { Console.WriteLine(name); } Console.WriteLine(); names.Add("John"); names.Add("Jim"); names.Add("Jack"); names.Add("Denny"); Console.WriteLine("Second iteration"); foreach (string name in namesWithJ) { Console.WriteLine(name); }
namesWithJ 一旦使用了ToArray、ToList之類的。就 names 了。如
var namesWithJ = (from n in names where n.StartsWith("J") orderby n select n).ToList();
標准的查詢操作
Enumberable 類定義的標准查詢操作符。
標准查詢操作符 |
說明 |
Where OfType<TResult> |
稱為篩選操作符定義返回元素的條件。 Where 使用謂詞,返回符合條件的元素。 OfType<TResult> 返回符合類型的元素。 |
Select SelectMany |
投射操作符用於把對象轉換為另一個類型的新對象。 Select 和 SelectMany 定義根據選擇器函數選擇結果值的投射。 |
OrderBy ThenBy OrderByDescending ThenByDescending Reverse |
排序操作符改變返回的元素的順序。 Orderby 升序排序。 OrderBydescending 降序排序。 TheBy 和 ThenByDescending 二次排序。 Reverse 反轉集合元素。 |
Join GroupJoin |
連接操作符。用於合並不直接相關的集合。 Join 根據鍵選擇器函數連接兩個集合。 GroupJoin 連接兩個集合。 |
GroupBy ToLookup |
組合操作符把數據放在組中。 GroupBy 組合公共鍵的元素。 Tookup 創建一個一對多字典,組合元素。 |
Any All Contains |
限定操作符,元素滿足指定的條件。 Any 滿足謂詞函數的函數。 All 所有元素是否都滿足謂詞函數。 Contains 檢查某個元素是否在集合中。 |
Take Skip TakeWhile SkipWhile |
分區操作符返回集合的子集。 Take 從集合提取元素個數。 Skip 跳過指定的元素個數,提取其他元素。 TakeWhile 提取條件為真的元素。 |
Distinct Union Intersect Except Zip |
Set操作符返回一個集合。 Distinct 刪除重復的元素。 Union 返回集合中唯一元素。 Intersect 返回兩個集合都有的元素。 Except 只出現在一個集合中的元素。 Zip 兩個集合合並為一個元素。 |
First FirstOrDefault Last LastOrDefault ElementAt ElementAtOrDefault Single SingleOrDefault |
元素操作符返回一個元素。 First 返回第一個滿足條件的元素。 FirstOrDefault 類似First,如果未找到滿足條件元素,返回類型的默認值。 Last 返回最后一個滿足條件的元素。 ElementAt 返回元素的位置。 Single 返回一個滿足條件的元素。如果有多個元素都滿足條件,就拋出一個異常。 |
Count Sum Min Max Average Aggregate |
聚合操作符計算集合值。 Sum 總和。 Count 所有元素個數。 Min 最小元素。 Max 最大元素。 Average 平均值。 Aggregate 根據輸入的表達式獲取聚合值。 |
ToArray AsEnumerable ToList ToDictionary Cast<TResult> |
轉換操作符。 |
Empty Range Repeat |
生成操作符。 Empty 空集合。 Range 返回一系列數字。 Repeat 返回始終重復一直的集合。 |
篩選
where子句合並多個表達式。
var racers = from r in Formula1.GetChampions() where r.Wins > 15 && (r.Country == "Brazil" || r.Country == "Austria") select r; foreach (var racer in racers) { Console.WriteLine("{0:A}", racer); }
下面代碼有Where擴展方法Where和Select調用。
var racers = Formula1.GetChampions(). Where(r => r.Wins > 15 && (r.Country == "Brazil" || r.Country == "Austria")). Select(r => r);
索引器篩選
索引是篩選器返回的每個結果的計數器。
下面由Where擴展方法調用, 使用索引返回。
var racers = Formula1.GetChampions(). Where((r, index) => r.Wins > 15 && index % 2 != 0);
類型篩選
基於類型篩選,使用 OfType 擴展方法。
object[] data = {"one", 1, 2, "li"}; var query = data.OfType<int>(); foreach (var intValue in query) { Console.WriteLine("{0}", intValue); }
復合的from子句
var ferrariDrivers = from r in Formula1.GetChampions() from c in r.Cars where c == "Ferrari" orderby r.LastName select r.FirstName + " " + r.LastName; foreach (string name in ferrariDrivers) { Console.WriteLine("{0}", name); }
C#編譯器把復合的from子句和LINQ查詢轉換為SelectMany擴展方法。SelectMany方法用於迭代序列的序列。
var ferrariDrivers = Formula1.GetChampions(). SelectMany( r => r.Cars , (r,c) => new { Racer = r, Car = c }). Where(r => r.Car == "Ferrari"). OrderBy(r => r.Racer.LastName). Select(r => r.Racer.FirstName + " " + r.Racer.LastName);
排序
對序列排序,前面使用了orderby 子句。使用兩種方法。
var racers = from r in Formula1.GetChampions() where r.Country == "Brazil" orderby r.Wins descending select r; var racers2 = Formula1.GetChampions().Where(r => r.Country == "Brazil").OrderByDescending(r => r.Wins).Select(r => r);
OrderBy() 和 OrderByDescending() 方法返回 IOrderEnumerable<Tsource> 這個接口派生自IEnumerable<TSource>接口,包含一個額外的方法 CreateOrderedEnumerable<TSource>()。這個方法用於進一步非序列排序。根據關鍵字選擇器來排序,可以使用ThenBy() 和 ThenByDescending() 方法繼續排序。 這兩個方法 需要 IOrderEnumerable<TSource> 接口才能工作,但也返回這個接口。所以添加任意多個ThenBy() 和 ThenByDescending() 方法,對集合排序。
Linq查詢時,需要把所有用於排序的不同關鍵字 用逗號 分割開。 添加到 orderby 子句。 擴展方法 Take 提取前面 10 個元素。
var racers = (from r in Formula1.GetChampions() orderby r.Country, r.LastName, r.FirstName select r).Take(10); foreach (var item in racers) { Console.WriteLine(item); }
也可以使用 OrderBy() 和 ThenBy() 擴展方法執行相同的操作:
var racers = (from r in Formula1.GetChampions() orderby r.Country, r.LastName, r.FirstName select r).Take(10); foreach (var item in racers) { Console.WriteLine(item); } Console.WriteLine("***********"); var racers2 = Formula1.GetChampions(). OrderBy(r => r.Country). ThenBy(r => r.LastName). ThenBy(r => r.FirstName). Take(10); foreach (var item in racers2) { Console.WriteLine(item); }
分組
根據一個關鍵字值對查詢結果進行分組,使用 group 子句。
子句 group r by r.Country into g 根據 Country 屬性組合。並定義一個新的標識符g。它以后用於訪問分組的結果信息。
var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key where g.Count() >= 2 select new { Country = g.Key, Count = g.Count() }; foreach (var country in countries) { Console.WriteLine(format: "{0,-10} {1}", arg0: country.Country, arg1: country.Count); }
使用擴展方法,子句 group r by r.Country into g 解析為 GroupBy(r => r.Country) 返回分組序列。
var contries2 = Formula1.GetChampions(). GroupBy(r => r.Country). OrderByDescending(g => g.Count()). ThenBy(g => g.Key). Where(g => g.Count() >= 2). Select(g => new { Country = g.Key, Count = g.Count() });
對嵌套的對象分組
分組的對象包含嵌套的序列,可以改變 select 子句創建的匿名類型。
var countries = from r in Formula1.GetChampions() group r by r.Country into g orderby g.Count() descending, g.Key where g.Count() >= 2 select new { Country = g.Key, Count = g.Count(), Racers = from racer in g orderby racer.LastName select racer.FirstName + " " + racer.LastName }; foreach (var country in countries) { Console.WriteLine(format:"{0, -10} {1}", arg0:country.Country, arg1:country.Count); foreach (var racer in country.Racers) { Console.WriteLine(format:"{0}; ", arg0:racer); } Console.WriteLine(); }
內連接
使用 join 子句 根據特定的條件合並兩個數據源,但之前要獲得兩個要連接的列表。
var racers = from r in Formula1.GetChampions() from y in r.Years select new { Year = y, Name = r.FirstName + " " + r.LastName }; var teams = from t in Formula1.GetContructorChampions() from y in t.Years select new { Year = y, Name = t.Name }; //var racersAndTeams = // (from r in racers // join t in teams on r.Year equals t.Year // orderby t.Year // select new // { // Year = r.Year, // Champion = r.Name, // Constructor = t.Name // }).Take(10); var racersAndTeams = (from r in from r1 in Formula1.GetChampions() from yr in r1.Years select new { Year = yr, Name = r1.FirstName + " " + r1.LastName } join t in from t1 in Formula1.GetContructorChampions() from yt in t1.Years select new { Year = yt, Name = t1.Name } on r.Year equals t.Year orderby t.Year select new { Year = r.Year, Champion = r.Name, Constructor = t.Name }).Take(10); Console.WriteLine("Year World Champion\t Constructor Title"); foreach (var item in racersAndTeams) { Console.WriteLine("{0}: {1,-20} {2}", item.Year, item.Champion, item.Constructor); }
左外連接
左外連接返回左邊序列中的全部元素,即使它們在右邊的序列中並沒有匹配的元素。
左外連接用join子句和 DefaultIfEmpty 方法定義。 使用 DefaultIfEmpty 定義其右側的默認值。
var racers = from r in Formula1.GetChampions() from y in r.Years select new { Year = y, Name = r.FirstName + " " + r.LastName }; var teams = from t in Formula1.GetContructorChampions() from y in t.Years select new { Year = y, Name = t.Name }; var racersAndTeams = (from r in racers join t in teams on r.Year equals t.Year into rt from t in rt.DefaultIfEmpty() orderby r.Year select new { Year = r.Year, Champion = r.Name, Constructor = t == null ? "no constructor championship" : t.Name }).Take(10); Console.WriteLine("Year Champion\t\t Constructor Title"); foreach (var item in racersAndTeams) { Console.WriteLine("{0}: {1,-20} {2}", item.Year, item.Champion, item.Constructor); }
組連接
左外連接使用了組連接和 into 子句。它有一部分與組連接相同,只不過組連接不適用 DefaultIfEmpty 方法。 使用組連接時,可以連接兩個獨立的序列,對於其中一個序列中的某個元素,另一個序列中存在對應項列表。
var racers = Formula1.GetChampionships() .SelectMany(cs => new List<RacerInfo>() { new RacerInfo { Year = cs.Year, Position = 1, FirstName = cs.First.FirstName(), LastName = cs.First.LastName() }, new RacerInfo { Year = cs.Year, Position = 2, FirstName = cs.Second.FirstName(), LastName = cs.Second.LastName() }, new RacerInfo { Year = cs.Year, Position = 3, FirstName = cs.Third.FirstName(), LastName = cs.Third.LastName() } }); var q = (from r in Formula1.GetChampions() join r2 in racers on new { FirstName = r.FirstName, LastName = r.LastName } equals new { FirstName = r2.FirstName, LastName = r2.LastName } into yearResults select new { FirstName = r.FirstName, LastName = r.LastName, Wins = r.Wins, Starts = r.Starts, Results = yearResults }); foreach (var r in q) { Console.WriteLine("{0} {1}", r.FirstName, r.LastName); foreach (var results in r.Results) { Console.WriteLine("{0} {1}", results.Year, results.Position); } }
集合操作
擴展方法 Distinct()、 Union()、Intersect() 和 Except() 都是集合操作。
Func<string, IEnumerable<Racer>> racersByCar = car => from r in Formula1.GetChampions() from c in r.Cars where c == car orderby r.LastName select r; Console.WriteLine("World champion with Ferrari and McLaren"); foreach (var racer in racersByCar("Ferrari").Intersect(racersByCar("McLaren"))) { Console.WriteLine(racer); }
集合操作通過調用實體類的 GetHashCode() 和 Equals() 方法比較對象。 對於自定義比較,可以傳遞實現 IEqualityComparer<T>接口的對象。
合並
Zip 方法。 .Net 4.0 新加的,用一個謂詞函數把兩個相關的序列合並為一個。
var racerNames = from r in Formula1.GetChampions() where r.Country == "Italy" orderby r.Wins descending select new { Name = r.FirstName + " " + r.LastName }; var racerNamesAndStarts = from r in Formula1.GetChampions() where r.Country == "Italy" orderby r.Wins descending select new { LastName = r.LastName, Starts = r.Starts }; var racers = racerNames.Zip(racerNamesAndStarts, (first, second) => first.Name + ", starts: " + second.Starts); foreach (var r in racers) { Console.WriteLine(r); }
分區
擴展方法 Take() 和 Skip() 等的分區操作可以用於分頁。
Skip() 忽略根據頁面大小和實際頁數計算出的項數。
Take() 根據頁面大小提取一定數量的項。
int pageSize = 5; int numberPages = (int)Math.Ceiling(Formula1.GetChampions().Count() / (double)pageSize); for (int page = 0; page < numberPages; page++) { Console.WriteLine("Page {0}", page); var racers = (from r in Formula1.GetChampions() orderby r.LastName, r.FirstName select r.FirstName + " " + r.LastName). Skip(page * pageSize).Take(pageSize); foreach (var name in racers) { Console.WriteLine(name); } Console.WriteLine(); }
對應的擴展方法 TakeWhile() 和 SkipWhile() 。
聚合操作符
聚合操作符(如 Count()、Sum()、Min()、Max()、Average() 、Aggregate() )返回一個值。
Count 返回集合項數。
var query = from r in Formula1.GetChampions() let numberYears = r.Years.Count() where numberYears >= 3 orderby numberYears descending, r.LastName select new { Name = r.FirstName + " " + r.LastName, TimesChampion = numberYears }; foreach (var r in query) { Console.WriteLine("{0} {1}", r.Name, r.TimesChampion); }
Sum 序列中的所有數字的和。
var countries = (from c in from r in Formula1.GetChampions() group r by r.Country into c select new { Country = c.Key, Wins = (from r1 in c select r1.Wins).Sum() } orderby c.Wins descending, c.Country select c).Take(5); foreach (var country in countries) { Console.WriteLine("{0} {1}", country.Country, country.Wins); }
Min 返回集合中的最小值。
Max 返回集合中的最大值。
Average 返回集合中的平均值。
Aggregate 傳遞一個 lambda 表達式,該表達式對所有的值進行聚合。
轉換操作符
查詢可以推遲到訪問數據項時再執行。在迭代中使用查詢時,查詢會執行。而使用轉換操作符會立即執行查詢,把查詢結果放在數組、列表或字典中。
ToList() 立即執行查詢,結果放在 List<T> 類中。
List<Racer> racers = (from r in Formula1.GetChampions() where r.Starts > 150 orderby r.Starts descending select r).ToList(); foreach (var racer in racers) { Console.WriteLine("{0} {0:S}", racer); }
也可以用 ToLookup<TKey, TElement> 類中,鍵可以對應多個值。
ToDictionary 支持鍵對應一個值。
var racers = (from r in Formula1.GetChampions() from car in r.Cars select new { Car = car, Racer = r } ).ToLookup(cr => cr.Car, cr => cr.Racer); if (racers.Contains("Williams")) { foreach (var williamsRacer in racers["Williams"]) { Console.WriteLine(williamsRacer); } }
Cast 在非類型化的集合上查詢
var list = new System.Collections.ArrayList(Formula1.GetChampions() as System.Collections.ICollection); var query = from r in list.Cast<Racer>() where r.Country == "USA" orderby r.Wins descending select r; foreach (var racer in query) { Console.WriteLine("{0:A}", racer); }
生成操作符
生成操作符 Range()、Empty() 和 Repear() 是返回序列的正常靜態方法。
在 Linq to Objects 中,這些方法可用於 Enumerable 類。
如 需要填充一二范圍的數字,此時就應使用 Range() 方法。這個方法第一個參數作為起始值,把第二個參數作為要填充的項數。
var values = Enumerable.Range(1, 20); foreach (var value in values) { Console.WriteLine(value); }
Range() 方法不返回填充所定義值的集合,與其他方法一樣,推遲查詢,返回一個 RangeEnumerator。其中用 yield return 語句,來遞增值。
該結果也可以與其他擴展方法一起用。
var values = Enumerable.Range(1, 20).Select(n => n * 3); foreach (var value in values) { Console.WriteLine(value); }
Empty() 方法返回一個不返回值的迭代器,用於需要一個集合的參數,可以給參數傳遞空集合。
string[] names1 = { "Hartono, Tommy" }; string[] names2 = { "Adams, Terry", "Andersen, Henriette Thaulow", "Hedlund, Magnus", "Ito, Shu" }; string[] names3 = { "Solanki, Ajay", "Hoeing, Helge", "Andersen, Henriette Thaulow", "Potra, Cristina", "Iallo, Lucio" }; List<string[]> namesList = new List<string[]> { names1, names2, names3 }; IEnumerable<string> allNames = namesList.Aggregate(Enumerable.Empty<string>(), (current, next) => next.Length > 3 ? current.Union(next) : current); foreach (string name in allNames) { Console.WriteLine(name); }
Repeat() 方法 返回一個迭代器,把同一個值重復特定的次數。
IEnumerable<string> strings = Enumerable.Repeat("I like programming.", 15); foreach (String str in strings) { Console.WriteLine(str); }
並行Linq
System.Linq 名稱空間中的包含的類 ParallelEnumerable 可以分解查詢的工作, 使其分布在多個線程上。盡管 Enumerable 類給 IEnumerable<T> 接口定義了擴展方法,但 ParallelEnumerable 類的大多數擴展方法是 ParallelQuery<TSource>類的擴展。一個重要的例外是 AsParallel() 方法,擴展 IEnumerable<TSource> 接口,返回 ParallelQuery<TSource>類,所以正常的集合類可以以平行方式查詢。
static IEnumerable<int> SampleData() { const int arraySize = 100000000; var r = new Random(); return Enumerable.Range(0, arraySize).Select(x => r.Next(140)).ToList(); } var data = SampleData();
var res = (from x in data.AsParallel() where Math.Log(x) < 4 select x).Average();
// 修改后的語法 var res2 = data.AsParallel().Where(x => Math.Log(x) < 4).Select(x => x).Average();
與Linq查詢一樣,編譯器會修改語法,調用AsParallel()、Where()、Select()、Average()。 Asparallel() 方法調用用 ParallelEnumerable 類定義,擴展 IEnumberable<T>接口,所以可以對簡單的數組調用它。 AsParallel() 方法返回 ParallelQuery<TSource>。因為返回的類型,所以編譯器選擇的Where()方法是ParallelEnumerable.Where(),而不是 Enumerable.Where()。 其他的 Select 和 Average 也是來自 ParallelEnumerable 類。與 Enumerable 類相反,對於 ParllelEnumerable類,查詢是分區的,以便多個線程可以同時處理該查詢。集合可以分為多個部分,其中每個部分由不同的線程處理,以篩選其余項。完成分區的工作后,就需要合並,獲得所有部分的總和。
運行時,可以打開任務管理器,就可以發現所有的CPU都處於忙碌的狀態。
分區器
AsParallel()方法不僅擴展了 IEnumerable<T> 接口,還擴展了 Partitioner 類。通過它,可以影響創建的分區。
手動創建一個分區器
var result = (from x in Partitioner.Create(data).AsParallel() where Math.Log(x) < 4 select x).Average();
也可以調用WithExecutionMode() 和 WithDegreeOfParallelism() 方法,影響並行機制。對於 WithExecutionMode() 方法傳遞 ParallelExecutionMode的一個Default值或者 ForceParallelism值。默認情況下,並行Linq避免使用系統開銷很高的並行機制。對於WithDegreeOfParallelism()方法,可以傳遞一個整數值,制定並行運行的最大任務數。
取消
.NET 提供一個標准方法,來取消長時間運行的任務,也適用於並行Linq。
要取消長時間運行的查詢可以給查詢添加WithCancellation() 方法,並傳遞一個 CancellactionToken令牌作為參數。CancelllationToken令牌從CancellactionTokenSource類中創建。該查詢在單獨的線程中運行,在該線程中,捕獲一個OperationCanceledException類型的異常。如果取消了查詢就出發這個異常。在主線程中,調用CancellationTokenSource類的Cancel()方法可以取消任務。
var data = SampleData(); CancellationTokenSource cts = new CancellationTokenSource(); Task.Factory.StartNew(() => { try { var res = (from x in data.AsParallel().WithCancellation(cts.Token) where Math.Log(x) < 4 select x).Average(); Console.WriteLine("query finished, sum:{0}",res); } catch (OperationCanceledException ex) { Console.WriteLine("canceled!"); Console.WriteLine(ex.Message); } }); string input = Console.ReadLine(); if (input.ToLower().Equals("y")) { cts.Cancel(); Console.WriteLine("canceled 2!"); }
表達式樹
擴展方法需要將一個委托類型作為參數,這就可以將 lambda 表達式賦予參數。 lambda 表達式賦予 Expression<T>類型的參數。C#編譯器根據類型給 lambda表達式定義不同的行為。如果類型是 Express<T>,編譯器就從 lambda 表達式中創建一個表達式樹,並存儲在程序集中。這樣,就可以在運行期間分析表達式樹,並進行優化,以便於查詢數據源。
http://www.cnblogs.com/mcgrady/archive/2014/05/17/3732694.html#_label5
private static void DisplayTree(int indent, string message, Expression expression) { string output = String.Format("{0} {1} ! NodeType: {2}; Expr: {3} ", "".PadLeft(indent, '>'), message, expression.NodeType, expression); indent++; switch (expression.NodeType) { case ExpressionType.Lambda: Console.WriteLine(output); LambdaExpression lambdaExpr = (LambdaExpression)expression; foreach (var parameter in lambdaExpr.Parameters) { DisplayTree(indent, "Parameter", parameter); } DisplayTree(indent, "Body", lambdaExpr.Body); break; case ExpressionType.Constant: ConstantExpression constExpr = (ConstantExpression)expression; Console.WriteLine("{0} Const Value: {1}", output, constExpr.Value); break; case ExpressionType.Parameter: ParameterExpression paramExpr = (ParameterExpression)expression; Console.WriteLine("{0} Param Type: {1}", output, paramExpr.Type.Name); break; case ExpressionType.Equal: case ExpressionType.AndAlso: case ExpressionType.GreaterThan: BinaryExpression binExpr = (BinaryExpression)expression; if (binExpr.Method != null) { Console.WriteLine("{0} Method: {1}", output, binExpr.Method.Name); } else { Console.WriteLine(output); } DisplayTree(indent, "Left", binExpr.Left); DisplayTree(indent, "Right", binExpr.Right); break; case ExpressionType.MemberAccess: MemberExpression memberExpr = (MemberExpression)expression; Console.WriteLine("{0} Member Name: {1}, Type: {2}", output, memberExpr.Member.Name, memberExpr.Type.Name); DisplayTree(indent, "Member Expr", memberExpr.Expression); break; default: Console.WriteLine(); Console.WriteLine("{0} {1}", expression.NodeType, expression.Type.Name); break; } } static void Main() { Expression<Func<Racer, bool>> expression = r => r.Country == "Brazil" && r.Wins > 6; DisplayTree(0, "Lambda", expression); }
LINQ提供程序
LINQ提供程序為特定的數據源實現了標准的查詢操作符。LINQ提供程序也許會實現比LINQ定義的更多擴展方法,但至少要實現標准操作符。LINQ to XML 如 Extensions 類定義 Elements()、Descendants() 和 Ancestors() 。
LINQ提供程序的實現方案是根據名稱空間和第一個參數的類型來選擇的。如 LINQ to Objects 定義 Where() 方法的參數 和 在 LINQ to Entities 中定義的 Where() 的方法參數不同。