查詢是一種從數據源檢索數據的表達式。查詢通常用專門的查詢語言來表示 。隨著時間的推移,人們已經為各種數據源開發了不同的語言;例如,用於關系 數據庫的 SQL 和用於 XML 的 XQuery。因此,開發人員不得不針對他們必須支 持的每種數據源或數據格式而學習新的查詢語言。LINQ 通過提供一種跨各種數 據源和數據格式使用數據的一致模型,簡化了這一情況。在 LINQ 查詢中,始終 會用到對象。可以使用相同的基本編碼模式來查詢和轉換 XML 文檔、SQL數據庫、ADO.NET 數據集、.NET 集合中的數據以及對其有 LINQ 提供程序可用的任 何其他格式的數據。
查詢操作的三個部分
所有 LINQ 查詢操作 都由以下三個不同的操作組成:
獲取數據源。
創建查詢。
執行查詢。
下面的示例演示如何用源代碼表示查詢操作的三個 部分。為了方便起見,此示例將一個整數數組用作數據源;但其中涉及的概念同 樣適用於其他數據源。本主題的其余部分也會引用此示例。
class IntroToLINQ
{
static void Main()
{
// The Three Parts of a LINQ Query:
// 1. Data source.
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
from num in numbers
where (num % 2) == 0
select num;
// 3. Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
}
}
下圖顯示了完整的查詢操作。在 LINQ 中,查詢的執行與查詢本 身截然不同;換句話說,如果只是創建查詢變量,則不會檢索任何數據。
數據源
在上一個示例中,由於數據源是數組,因此它隱式支 持泛型 IEnumerable<(Of <(T>)>) 接口。這一事實意味著該數據 源可以用 LINQ 進行查詢。在 foreach 語句中執行查詢,而 foreach 要求使用 IEnumerable 或 IEnumerable<(Of <(T>)>)。支持 IEnumerable<(Of <(T>)>) 或派生接口(如泛型 IQueryable< (Of <(T>)>))的類型稱為“可查詢類型”。
可查 詢類型不需要進行修改或特殊處理就可以用作 LINQ 數據源。如果源數據還沒有 作為可查詢類型出現在內存中,則 LINQ 提供程序必須以此方式表示源數據。例 如,LINQ to XML 將 XML 文檔加載到可查詢的 XElement 類型中:
// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load (@"c:myContactList.xml");
在 LINQ to SQL 中,首 先手動或使用 對象關系設計器(O/R 設計器) 在設計時創建對象關系映射。針 對這些對象編寫查詢,然後由 LINQ to SQL 在運行時處理與數據庫的通信。在 下面的示例中,Customer 表示數據庫中的特定表,並且 Table 支持派生自 IEnumerable<(Of <(T>)>) 的泛型 IQueryable<(Of < (T>)>) 接口。
DataContext db = new DataContext (@"c:northwindnorthwnd.mdf");
有關如何創建特定 類型的數據源的更多信息,請參見各種 LINQ 提供程序的文檔。但基本規則非常 簡單:LINQ 數據源是支持泛型 IEnumerable<(Of <(T>)>) 接口或 從該接口繼承的接口的任意對象。
查詢
查詢指定要從數據源中 檢索的信息。查詢還可以指定在返回這些信息之前如何對其進行排序、分組和結 構化。查詢存儲在查詢變量中,並用查詢表達式進行初始化。為使編寫查詢的工 作變得更加容易,C# 引入了新的查詢語法。
上一個示例中的查詢從整數 數組中返回所有偶數。該查詢表達式包含三個子句:from、where 和 select。 (如果您熟悉 SQL,您會注意到這些子句的順序與 SQL 中的順序相反。) from 子句指定數據源,where 子句應用篩選器,select 子句指定返回的元素的類型 。LINQ 查詢表達式(C# 編程指南)一節中詳細討論了這些子句和其他查詢子句 。目前需要注意的是,在 LINQ 中,查詢變量本身不執行任何操作並且不返回任 何數據。它只是存儲在以後某個時刻執行查詢時為生成結果而必需的信息。有關 在幕後是如何構建查詢的更多信息,請參見標准查詢運算符概述。
查詢 執行
延遲執行
如前所述,查詢變量本身只是存儲查詢命令。實 際的查詢執行會延遲到在 foreach 語句中循環訪問查詢變量時發生。此概念稱 為“延遲執行”,下面的示例對此進行了演示:
// Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
foreach 語 句也是檢索查詢結果的地方。例如,在上一個查詢中,迭代變量 num 保存了返 回的序列中的每個值(一次保存一個值)。
由於查詢變量本身從不保存 查詢結果,因此可以根據需要隨意執行查詢。例如,可以通過一個單獨的應用程 序持續更新數據庫。在應用程序中,可以創建一個檢索最新數據的查詢,並可以 按某一時間間隔反復執行該查詢以便每次檢索不同的結果。
強制立即執 行
對一系列源元素執行聚合函數的查詢必須首先循環訪問這些元素。 Count、Max、Average 和 First 就屬於此類查詢。由於查詢本身必須使用 foreach 以便返回結果,因此這些查詢在執行時不使用顯式 foreach 語句。另 外還要注意,這些類型的查詢返回單個值,而不是 IEnumerable 集合。下面的 查詢返回源數組中偶數的計數:
var evenNumQuery =
from num in numbers
where (num % 2) == 0
select num;
int evenNumCount = evenNumQuery.Count();
若要強 制立即執行任意查詢並緩存其結果,可以調用 ToList<(Of < (TSource>)>) 或 ToArray<(Of <(TSource>)>) 方法。
List<int> numQuery2 =
(from num in numbers
where (num % 2) == 0
select num).ToList ();
// or like this:
// numQuery3 is still an int[]
var numQuery3 =
(from num in numbers
where (num % 2) == 0
select num).ToArray();
此外,還可以通過在緊跟 查詢表達式之後的位置放置一個 foreach 循環來強制執行查詢。但是,通過調 用 ToList 或 ToArray,也可以將所有數據緩存在單個集合對象中。