LINQ首部曲: LINQ To Object Part 2 - Using VB.NET

LINQ 首部曲: LINQ To Object Part 2 - Using VB.NET
文/黃忠成
續前文,這一篇一樣是發表於Run! PC雜誌,同時收錄於【極意之道-.NET Framework 3.5資料庫開發聖典-ASP.NET篇】一書中,於此我針對VB.NET做了調整。
回到起點: LINQ 語法規則
任何語言皆有其基本語法規則存在,LINQ雖然被打造成一個與編譯器無關的Framework,但為了讓設計師能更方便、更直覺的使用LINQ Expression,目前已知的C#、VB.NET都定義了一套專屬的LINQ Expression語法,雖然各個編譯器所提供的語法略有差異,但基本上差距不遠,本節以VB.NET所提供的LINQ Expression為基準,為讀者們介紹LINQ Expression的語法規則及各式各樣的應用,表1是筆者所整理出來的VB.NET LINQ Expression語法規則。
[ 表1]
u query-expression
From <alias> In <source|query-expression> <<where-expression>|<group-by expression>|<join expression>|<order-by-expression>><select-expression>
u where-expression
left-variable<operand>right-variable
n operand
(= | <> | < | > | >= | <=)
left-variable<operand>query-expression(single valueresult)
query-expression(single valueresult) <operand>left-variable
query-expression(single valueresult) <operand>query-expression(single valueresult)
variable.<function>(Boolean result)
variable.<function> operand right-variable
variable.<function>operand variable<function>
<shared function> operand right-varaible
<shared function> operand <shared function>
<function> operand right-varaible
<function> operand <shared function>
<VB.NET Expression single line>(Boolean result)
<VB.NET Expression single line> operand right-variable
<VB.NET Expression single line>operand <VB.NET Expression single line>
where-expression < And | Or | AndAlso | OrElse>, where-expression <*>
u group-by expression
group <alias | query-expression> by <alias.member|query-expression(single value result)>into <variable>
group <alias | query-expression> by <alias.member| query-expression(single value result)>,<alias.member| query-expression(single value result)>into <variable>
u join-expression
join <alias > in <source|queryt-expression> on<where-expression(equal only)>
join <alias > in <source|queryt-expression> on<where-expression(equal only)> into <variable>
u order-by-expression
order by <alias-member><asc|desc>
order by <alias-member>,<alias-member><asc|desc>
u select-expression
Select <alias member>
Select new With {anonymoustype declaration}
乍看之下,此表似乎相當複雜,其實她還蠻簡單的,以此表搭配下文的語法分類解說,應能讓讀者們快速的掌握VB.NET中的LINQ Expression語法。
Query-Expression
LINQ Expression 中至少會包含一個query-expression,表一中對query-expression的定義如下。
From <alias> In <source|query-expression> <<where-expression>|<group-by expression>|<join expression>|<order-by-expression>><select-expression>
<alias> 指的是欲在此query-expression中使用的別名,更明確說是一個變數,編譯器會將In後面的運算式所產生的結果放到此變數中,此處有一點必須特別指出,In後面的<source|query-exprssion>代表著來源,<soruce>可以是一個IEnumerable(Of T)類型的物件,法則上<source>也可以由另一個<query-exprssion>所取代,我們將此稱為Nested query-expression(巢狀Query)。Query-expression中可以包含Where語句<where-expression>、Group By語句<group expression>、Join語句<join expression>、Order By修飾句<order-by-exprssion>及Select語句<select-expression>,因應不同的語句,其內或允許query-exprssion的出現,下例是一個簡單的query-expression。
Dim list() As String = {"1111", "2222", "3333"}
Dim p = From o In list Select o
Where-expression
Query-expression 中可以包含where-expression,用來對物件作查詢動作,下例是一個簡單的例子。
Dim list() As String = {"1111", "2222", "3333"}
Dim p = From o In list Where o = "2222" Select o
如表一所示,where-expression的left-expression及 right-expression也可以由query-expression所取代,前提是這個query-expression必須傳回單一值,如程式1。
[ 程式1]
Sub TestComplexWhere2()
Dim p1() = { _
New With {.Name = "code6421", .Address = "Taipai"}, _
New With {.Name = "tom", .Address = "Taipai"}, _
New With {.Name = "jeffray", .Address = "NY"} _
}
Dim p2() = { _
New With {.Name = "code6421", .Title = "Manager"}, _
New With {.Name = "tom", .Title = "Director"}, _
New With {.Name = "jeffray", .Title = "Programmer"} _
}
Dim p3() = { _
New With {.Name = "code6421", .Hand = "Manager", .Address = "Taipai"}, _
New With {.Name = "tom", .Hand = "Director", .Address = "Taipai"}, _
New With {.Name = "jeffray", .Hand = "Programmer", .Address = "Taipai"} _
}
Dim result = From s In p2 _
Where (From s1 In p1 Where s1.Name = s.Name Select s1.Address).ToArray()(0) = _
(From s2 In p3 Where s2.Name = s.Name Select s2.Address).ToArray()(0) Select s
For Each item In result
Console.WriteLine(item.Name)
Next
Console.ReadLine()
End Sub
ToArray 函式是LINQ To Object Framework所提供的函式,用途是將query-expression的結果轉成陣列,一旦轉成陣列後就可以透過()陣列元素存取值去取得單值。
當然,基於LINQ Expression的轉譯規則,你也可以使用傳回Boolean值的函式來協助比對。
Sub TestWhereWithFunction()
Dim list() As String = {"code6421", "tom", "cathy"}
Dim result = From s1 In list Where MyExpressionFunc(s1) Select s1
End Sub
Function MyExpressionFunc(ByVal s As String) As Boolean
Return IIf(s = "code6421", True, False)
End Function
這個函式可以是成員函式(需要有物件、靜態函式,或是位於Module中的函式皆可。
Group-expression
Query-expression 中允許含有Group-expression,用於將資料分類用,如程式2。
[ 程式2]
Sub TestGroupByLinq()
Dim persons() = { _
New With {.Name = "code6421", .Age = 18, .Address = "Taipai"}, _
New With {.Name = "jeffray", .Age = 18, .Address = "USA"}, _
New With {.Name = "catch", .Age = 18, .Address = "USA"}, _
New With {.Name = "joe", .Age = 18, .Address = "NY"} _
}
Dim result = From o In persons Group o By o.Address Into g = Group _
Select New With {.Address = Address, .Persons = g}
For Each item In result
Console.WriteLine("Group : {0}", item.Address)
For Each detailItem In item.Persons
Console.WriteLine("{0}", detailItem.Name)
Next
Next
Console.ReadLine()
End Sub
此例執行結果如圖1。
[ 圖1]
Group : Taipai
code6421
Group : USA
jeffray
catch
Group : NY
joe
此處有兩個尚未提及的指令,Into是將Group後的結果放到g變數中,於VB.NET中,你必須指定g的值為Group關鍵字,此處Group關鍵字的內涵值為Group後的結果,是一個IEnumerable(Of Object)的物件。Select New With則是應用了VB.NET 2008的Anonymous Type技巧,建立一個簡單的物件,其中含有Address及Persons兩個屬性,Address的值來自於Address,也就是Group o By o.Address的鍵值,那就是Address的值。使用Select New With時,記得屬性名稱需以【.】作為前導字,如下:
Select New With {.Name = "code6421", .Age = 18, .Address = "Taipai"},
New With 除了可放在Select之後,也可直接用來建立Anonymous Type,如下所示:
New With {.Name = "code6421", .Age = 18, .Address = "Taipai"}
而g本身是一個分類後的IEnumerable(Of Object)類型物件,其內所含的就是以Address值所分類後的Persons物件。與where-expression一樣,group-expression中也可以包含query-expression,如程式4。
[ 程式4]
Sub TestComplexGroupBy2()
Dim p2() = { _
New With {.Name = "code6421", .Hand = "Manager", .Address = "Taipai"}, _
New With {.Name = "tom", .Hand = "Director", .Address = "Taipai"}, _
New With {.Name = "jeffray", .Hand = "Programmer", .Address = "NT"} _
}
Dim result = From s In p2 Group s By Address = (From s1 In p2 Where _
s.Name = s1.Name Select s1.Address).ToArray()(0) Into g = Group _
Select New With {.Address = Address, .Persons = g}
For Each item In result
Console.WriteLine("Group : {0}", item.Address)
For Each detailItem In item.Persons
Console.WriteLine("{0}", detailItem.Name)
Next
Next
Console.ReadLine()
End Sub
執行結果如圖2。
[ 圖2]
Group : Taipai
code6421
tom
Group : NT
jeffray
Join-expression
LINQ Expression 也支援SQL中常用的Join指令,如程式5。
[ 程式5]
Sub TestJoin()
Dim p1() = { _
New With {.Name = "code6421", .Address = "Taipai"}, _
New With {.Name = "tom", .Address = "Taipai"}, _
New With {.Name = "jeffray", .Address = "NY"} _
}
Dim p2() = { _
New With {.Name = "code6421", .Title = "Manager"}, _
New With {.Name = "tom", .Title = "Director"}, _
New With {.Name = "jeffray", .Title = "Programmer"} _
}
Dim result = From s In p1 _
Join s1 In p2 On s.Name Equals s1.Name _
Select New With {.Name = s.Name, .Address = s.Address, .Title = s1.Title}
For Each item In result
Console.WriteLine("Name : {0}, Address : {1}, Title : {2}", _
item.Name, item.Address, item.Title)
Next
Console.ReadLine()
End Sub
圖3是執行結果。
[ 圖3]
Name : code6421, Address : Taipai, Title : Manager
Name : tom, Address : Taipai, Title : Director
Name : jeffray, Address : NY, Title : Programmer
程式6是運用一個以上join的例子。
[ 程式6]
Sub TestJoin2()
Dim p1() = { _
New With {.Name = "code6421", .Address = "Taipai"}, _
New With {.Name = "tom", .Address = "Taipai"}, _
New With {.Name = "jeffray", .Address = "NY"} _
}
Dim p2() = { _
New With {.Name = "code6421", .Title = "Manager"}, _
New With {.Name = "tom", .Title = "Director"}, _
New With {.Name = "jeffray", .Title = "Programmer"} _
}
Dim p3() = { _
New With {.Name = "code6421", .Hand = "Left"}, _
New With {.Name = "tom", .Hand = "Left"}, _
New With {.Name = "jeffray", .Hand = "Right"} _
}
Dim result = From s In p1 _
Join s1 In p2 On s.Name Equals s1.Name _
Join s2 In p3 On s.Name Equals s2.Name _
Select New With {.Name = s.Name, .Address = s.Address, _
.Title = s1.Title, .Hand = s2.Hand}
For Each item In result
Console.WriteLine("Name : {0}, Address : {1}, Title : {2}, Hand : {3}", _
item.Name, item.Address, item.Title, item.Hand)
Next
Console.ReadLine()
End Sub
執行結果如圖4。
[ 圖4]
Name : code6421, Address : Taipai, Title : Manager, Hand : Left
Name : tom, Address : Taipai, Title : Director, Hand : Left
Name : jeffray, Address : NY, Title : Programmer, Hand : Right
Order-by-expression
Order-by-expression 用於將query-expression的結果集排序,如程式7。
[ 程式7]
Sub TestOrderLinq()
Dim p() = {"1111", "3333", "2222"}
Dim result = From s1 In p Order By s1 Descending Select s1
For Each item In result
Console.WriteLine(item)
Next
Console.ReadLine()
End Sub
圖5是執行結果。
[ 圖5]
3333
2222
1111
Descending 指的是倒序,Ascending則是正序,未加上Descending也未加上Ascending,則預設為Ascending,Order By可以有多個,彼此以 , 分接,每個都能有正倒序之修飾字,如下所示。
Sub TestOrderLinq2()
Dim p1() = { _
New With {.Name = "code6421", .Address = "Taipai"}, _
New With {.Name = "tom", .Address = "Taipai"}, _
New With {.Name = "jeffray", .Address = "NY"} _
}
Dim result = From s1 In p1 Order By s1.Name, s1.Address Descending Select s1
For Each item In result
Console.WriteLine(item.Name)
Next
Console.ReadLine()
End Sub
Select-expresssion
Select-expression 出現於query-expression的尾端,涵意是由結果集中取出資料,其中較特別的是,有時我們會使用Select New With語句由結果集中取出需要的欄位,建構出另一個型態的回傳物件,如同於join範例中的片段。
Dim result = From s In p1 _
Join s1 In p2 On s.Name Equals s1.Name _
Join s2 In p3 On s.Name Equals s2.Name _
Select New With _
{.Name = s.Name, .Address = s.Address, .Title = s1.Title, .Hand = s2.Hand}
這是運用了VB.NET 2008的Anonymouse Type功能所達到的。
Distinct
如同SQL中的Distinct一樣,LINQ To Object Framework中的Distinct函式允許設計師將一陣列中特定欄位值相同的部份,僅擷取出一代表值,見程式8。
[ 程式8]
Sub TestSelectDistinct()
Dim persons() = { _
New With {.Name = "code6421", .Age = 18, .Address = "Taipai"}, _
New With {.Name = "jeffray", .Age = 18, .Address = "USA"}, _
New With {.Name = "catch", .Age = 18, .Address = "USA"}, _
New With {.Name = "joe", .Age = 18, .Address = "NY"} _
}
Dim result = (From s1 In persons Select s1.Address).Distinct()
For Each item In result
Console.WriteLine(item)
Next
Console.ReadLine()
End Sub
此例的執行結果如圖6。
[ 圖6]
Taipai
USA
NY
此例僅列出一個Taipai,這就是Distinct函式的能力,相同值者僅取一代表值。
Select Many 功能
截至目前為止的例子皆以一個query-expression作為開始,事實上LINQ Expression支援query-expression的串接,名為Select Many功能,見程式9。
[ 程式9]
Sub TestSelectMany()
Dim persons() = { _
New With {.Name = "code6421", .Age = 18, .Address = "Taipai"}, _
New With {.Name = "jeffray", .Age = 18, .Address = "USA"}, _
New With {.Name = "catch", .Age = 18, .Address = "USA"}, _
New With {.Name = "joe", .Age = 18, .Address = "NY"} _
}
Dim p2() = { _
New With {.Title = "Director"}, _
New With {.Title = "Programmer"}, _
New With {.Title = "Manager"} _
}
Dim result = From s1 In persons _
From s2 In p2 _
Select New With {.Name = s1.Name, .Title = s2.Title}
For Each item In result
Console.WriteLine("Name : {0}, Title : {1}", item.Name, item.Title)
Next
Console.ReadLine()
End Sub
此例的執行結果如圖7。
[ 圖7]
Name : code6421, Title : Director
Name : code6421, Title : Programmer
Name : code6421, Title : Manager
Name : jeffray, Title : Director
Name : jeffray, Title : Programmer
Name : jeffray, Title : Manager
Name : catch, Title : Director
Name : catch, Title : Programmer
Name : catch, Title : Manager
Name : joe, Title : Director
Name : joe, Title : Programmer
Name : joe, Title : Manager
Index
LINQ To Object Framework 的LINQ Expression允許指定Index,什麼是Index呢?請看程式10。
[ 程式10]
border-right: black 1pt solid; padding-right: 5.4pt; border-top: black 1pt solid; padding-left: 5.4pt; padding-bottom: 0cm; border-left: black 1pt solid; width: 418.1pt; padding-top: 0cm; border-bottom: black 1p
分享到:
评论
lovnet
  • 浏览: 1326824 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

你可能感兴趣的:(.net,asp.net,vb,VB.NET,LINQ)