撰寫第一個 LINQ 查詢 (Visual Basic)
「查詢」(Query) 是一種從資料來源擷取資料的運算式,而且使用專用的查詢語言來表示。隨著時間演進,業界已針對不同類型的資料來源開發出不同語言,例如,關聯式資料庫的 SQL 以及 XML 的 XQuery。因此,應用程式開發人員需要針對每種支援的資料來源類型或資料格式學習新的查詢語言。
Language-Integrated Query (LINQ) 提供一致的模型來使用各種資料來源和格式的資料,從而簡化了上述情況。在 LINQ 查詢中,您所處理的一定是物件。不論您要查詢及轉換的資料是存在 XML 文件、SQL 資料庫、ADO.NET 資料集和實體 (Entity),還是 .NET Framework 集合,以及其他任何有可用 LINQ 提供者 (Provider) 的來源或格式中,都是使用相同的基本程式碼撰寫模式。本文件說明建立及使用基本 LINQ 查詢的三個階段。
如需相關示範影片,請參閱如何:開始使用 LINQ? (英文)。
查詢作業的三個階段
LINQ 查詢作業包含三個動作:
取得一個或多個資料來源。
建立查詢。
執行查詢。
在 LINQ 中,查詢的執行與建立是分開的。單純建立查詢時,並不會擷取任何資料。本主題稍後會詳細討論這一點。
下列範例說明查詢作業的三個部分。為了便於示範,這個範例會使用整數陣列做為快速方便的資料來源。不過,相同的概念也適用於其他資料來源。
注意事項 |
---|
在 專案設計工具、編譯頁 (Visual Basic)中,確定 [Option Infer] 設定為 [在]。 |
' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}
' Query creation.
Dim evensQuery = From num In numbers
Where num Mod 2 = 0
Select num
' Query execution.
For Each number In evensQuery
Console.Write(number & " ")
Next
輸出:
0 2 4 6
資料來源
因為上一個範例的資料來源是陣列,所以這個範例隱含地支援泛型 IEnumerable<T> 介面。這便是可以使用陣列做為 LINQ 查詢資料來源的原因。支援 IEnumerable<T> 或衍生的介面 (例如泛型 IQueryable<T> 的型別都 稱為可查詢型別。
陣列是一種隱含的可查詢型別,不需要進行修改或特殊處理,就可以當成 LINQ 資料來源使用。這也適用於支援 IEnumerable<T>,包括泛型 List<T>, Dictionary<TKey, TValue>的任何集合型別和其他類別在 .NET Framework 類別庫中。
如果資料來源沒有實作 IEnumerable<T>, LINQ 提供者需要實作 標準查詢運算子的 功能該資料來源的。例如,LINQ to XML 會處理將 XML 文件載入至可查詢 XElement 型別的工作,如下列範例所示。如需標準查詢運算子的詳細資訊,請參閱標準查詢運算子概觀。
' Create a data source from an XML document.
Dim contacts = XElement.Load("c:\myContactList.xml")
使用 LINQ to SQL 時,請先在設計階段利用手動方式或Object Relational Designer (O/R Designer),建立物件關聯對應。您可以針對物件撰寫查詢,而 LINQ to SQL 則會在執行階段處理與資料庫之間的通訊。在下列範例中,customers 表示資料庫中的特定資料表,而 Table<TEntity> 則支援泛型 IQueryable<T>。
' Create a data source from a SQL table.
Dim db As New DataContext("C:\Northwind\Northwnd.mdf")
Dim customers As Table(Of Customer) = db.GetTable(Of Customer)
如需如何建立特定資料來源類型的詳細資訊,請參閱各種 LINQ 提供者的文件。(如需這些提供者的清單,請參閱 LINQ (Language-Integrated Query))。基本規則十分簡單:LINQ 資料來源可以是任何支援泛型 IEnumerable<T> 介面或繼承它之介面的物件。
注意事項 |
---|
諸如 ArrayList 等支援非泛型 IEnumerable 介面的型別,也可以當成 LINQ 資料來源使用。如需使用 ArrayList 的範例,請參閱 HOW TO:使用 LINQ 查詢 ArrayList。 |
查詢
在查詢中,可以指定想要從資料來源擷取的資訊。也可以選擇指定該資訊在傳回之前的排序、分組或結構方式。為了能夠建立查詢,Visual Basic 在語言中併入了新的查詢語法。
下列範例中的查詢在執行時會傳回整數陣列 numbers 中的所有偶數。
' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}
' Query creation.
Dim evensQuery = From num In numbers
Where num Mod 2 = 0
Select num
' Query execution.
For Each number In evensQuery
Console.Write(number & " ")
Next
查詢運算式包含三個子句:From、Where 和 Select。基本查詢作業 (Visual Basic) 中會討論這些查詢運算式子句的特定功能和用途。如需詳細資訊,請參閱查詢 (Visual Basic)。請注意,在 LINQ 中,查詢定義通常會儲存在變數中,等稍後再執行。查詢變數 (例如,在上述範例中, evensQuery 必須是可查詢的型別。evensQuery 的型別是 IEnumerable(Of Integer),會使用區域型別推斷的編譯器中指定。
重要的是要記住,查詢變數本身並不會採取任何動作,也不會傳回任何資料,它只會儲存查詢定義。在上一個範例中,負責執行查詢的是 For Each 迴圈 (Loop)。
查詢執行
查詢執行與查詢建立是分開的。查詢建立會定義查詢,但是執行則是由不同的機制所觸發 (Trigger)。查詢可以在定義之後馬上執行 (「立即執行」(Immediate Execution)),或者先儲存好定義再於稍後執行 (「延後執行」(Deferred Execution))。
延後執行
典型的 LINQ 查詢與上一個範例中的查詢類似,而上一個範例中定義了 evensQuery。它會建立查詢,但是不會立即執行。相反地,它會將查詢定義儲存在查詢變數 evensQuery 中。稍後您會再執行查詢,方法通常是使用會傳回一系列值的 For Each 迴圈,或是套用 Count 或 Max 等標準查詢運算子。這個程序稱為「延後執行」(Deferred Execution)。
' Query execution that results in a sequence of values.
For Each number In evensQuery
Console.Write(number & " ")
Next
' Query execution that results in a single value.
Dim evens = evensQuery.Count()
如果有一系列值,則可以在 For Each 迴圈中使用反覆運算變數 (在上一個範例中是 number) 來存取擷取到的資料。因為查詢變數 evensQuery 保存的是查詢定義,而不是查詢結果,所以您可以隨心所欲地多次使用查詢變數來執行查詢。例如,有另一個應用程式會持續更新您應用程式中的資料庫。建立從該資料庫擷取資料的查詢之後,您就可以使用 For Each 迴圈重複執行查詢,每次都擷取最新的資料。
下列範例示範延後執行的運作方式。在定義 evensQuery2 並使用 For Each 迴圈加以執行之後 (如上面範例所示),資料來源 numbers 中的部分項目有所變更。於是又再次使用一個 For Each 迴圈執行 evensQuery2。第二次的結果會不同,原因是再次執行查詢的 For Each 迴圈使用的是 numbers 中的新值。
Dim numberArray() = {0, 1, 2, 3, 4, 5, 6}
Dim evensQuery2 = From num In numberArray
Where num Mod 2 = 0
Select num
Console.WriteLine("Evens in original array:")
For Each number In evensQuery2
Console.Write(" " & number)
Next
Console.WriteLine()
' Change a few array elements.
numberArray(1) = 10
numberArray(4) = 22
numberArray(6) = 8
' Run the same query again.
Console.WriteLine(vbCrLf & "Evens in changed array:")
For Each number In evensQuery2
Console.Write(" " & number)
Next
Console.WriteLine()
輸出:
Evens in original array:
0 2 4 6
Evens in changed array:
0 10 2 22 8
立即執行
在查詢的延後執行中,查詢定義會儲存在查詢變數中供稍後執行。在立即執行中,查詢會在定義時馬上執行。當您所套用的方法需要存取查詢結果中的個別項目時,就會觸發執行。立即執行通常是透過一個會傳回單一值的標準查詢運算子強制執行,例如 Count、Max、Average 和 First。這些標準查詢運算子會在套用時馬上執行查詢,以計算並傳回單一結果。如需會傳回單一值之標準查詢運算子的詳細資訊,請參閱彙總作業、項目作業和數量詞作業。
下列查詢會傳回整數陣列中的偶數計數。但不會儲存查詢定義,而 numEvens 是簡單 Integer。
Dim numEvens = (From num In numbers
Where num Mod 2 = 0
Select num).Count()
使用 Aggregate 方法可以達成相同的結果。
Dim numEvensAgg = Aggregate num In numbers
Where num Mod 2 = 0
Select num
Into Count()
您也可以在查詢 (立即) 或查詢變數 (延後) 上呼叫 ToList 或 ToArray 方法來強制執行查詢 (如下列程式碼所示)。
' Immediate execution.
Dim evensList = (From num In numbers
Where num Mod 2 = 0
Select num).ToList()
' Deferred execution.
Dim evensQuery3 = From num In numbers
Where num Mod 2 = 0
Select num
' . . .
Dim evensArray = evensQuery3.ToArray()
在前述範例中,evensQuery3 是查詢變數,而 evensList 是清單,evensArray 是陣列。
如果想要立即執行查詢,並將結果快取至單一集合物件中,則使用 ToList 或 ToArray 強制立即執行特別有用。如需這些方法的詳細資訊,請參閱轉換資料型別。
您也可以使用諸如 IEnumerable.GetEnumerator 方法等 IEnumerable 方法來執行查詢。
相關示範影片
影片 HOW TO:在 Visual Basic 中撰寫查詢