LINQ「三」部曲:
基本的查詢只有「三」步驟
查詢的物件卻有「三」大塊
這次的實作測試讓我深深的迷上了LINQ
以下是DEMO結果
◎以上程式範例LINQ1.aspx,如在頁框下不能操作,請開新視窗操作
程式碼
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load Me.Button1.Text = "LINQ TO Object 測試" Me.Button2.Text = "LINQ TO MS SQL 測試" Me.Button3.Text = "LINQ TO XML 測試" Me.Label1.Text = "" End Sub Protected Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click ' Step1:取得資料來源 Obtain the data source Dim product_listPrice As Array = {609, 610, 615, 623, 623, 655, 683} ' Step2:查詢建立階段 Create the query Dim queryCheaper = From listprice_item In product_listPrice Where listprice_item < 620 Select listprice_item ' Step3:查詢執行階段 Query execution For Each query_result In queryCheaper Me.Label1.Text &= (query_result & "、") Next End Sub Protected Sub Button2_Click(sender As Object, e As System.EventArgs) Handles Button2.Click ' Step1:取得資料來源 Obtain the data source Dim dbconnection As New System.Data.SqlClient.SqlConnection() dbconnection.ConnectionString = "Data Source=xxxxxxxx;Initial Catalog=xxxxx;Persist Security Info=True;User ID=xxxxxx;Password=xxxxxx" Dim dc As New System.Data.Linq.DataContext(dbconnection) Dim tablecheaper As System.Data.Linq.Table(Of product_table) = dc.GetTable(Of product_table)() ' Step2:查詢建立階段 Create the query Dim querycheaper = From listPrice_item In tablecheaper Where listPrice_item.價格 < 660 Select listPrice_item.商品名稱, listPrice_item.價格 ' Step3:查詢執行階段 Query execution Me.GridView1.DataSource = querycheaper Me.GridView1.DataBind() dc.Dispose() End Sub <System.Data.Linq.Mapping.Table(Name:="[SalesLT].Product")> Public Class product_table <System.Data.Linq.Mapping.Column(name:="Name")> Public 商品名稱 As String <System.Data.Linq.Mapping.Column(name:="ListPrice")> Public 價格 As Decimal End Class Protected Sub Button3_Click(sender As Object, e As System.EventArgs) Handles Button3.Click ' Step1:取得資料來源 Obtain the data source Dim path As String = HttpContext.Current.Request.MapPath("~/App_Data/") Dim xml As XElement = XElement.Load(path & "Product.xml") ' Step2:查詢建立階段 Create the query Dim querycheaper = From listprice_item In xml.Elements() Where CType(listprice_item.Element("ListPrice"), Decimal) < 500 Select New With {.Name = CType(listprice_item.Element("Name"), String), .ListPrice = CType(listprice_item.Element("ListPrice"), Decimal)} ' Step3:查詢執行階段 Query execution For Each query_result In querycheaper Me.Label1.Text &= String.Format("<p>商品名稱:{0} 價格:{1}</p>", query_result.Name, query_result.ListPrice) Next End Sub
範例說明:
上面的資料庫是使用微軟提供的AdventureWorks中的AdventureWorksLT2008,XML檔案也是依照它來仿寫,若是要使用XML請由此下載
從範例中,我們可以對三個資料來源的LINQ( (Language-Integrated Query))查詢作比較,每個資料來源的基本查詢步驟都包含:
Step1:取得資料來源 Obtain the data source
Step2:查詢建立階段 Create the query
Step3:查詢執行階段 Query execution從上圖,配合著程式碼來看,我們知道「查詢建立」和「查詢執行」階段是分開的,「查詢建立」的時期並不會擷取任何資料
差異中最小的是「Step2:查詢建立階段 Create the query」,即建立查詢時的語法結構,結構不外乎是三部分
(1) 指定資料來源 (From <element> In <collection>)
(2) 篩選資料 (Where)
(3) 選取資料 (Select)
這個階段用了很多的變數來當作仲介,所以需注意結構順序,from… where… select… 和 from… select…where 意義可以一樣,但會因為變數先定義、後定義產生不同的寫法。
別和我們學的T-SQL : SELECT…FROM…WHERE 搞混了。
語法結構和學習T-SQL一樣,有很多句型要學習。
「Step1:取得資料來源 Obtain the data source 」學習時我自已覺得比較吃力,主要有二部分
(1)取得連繫
可提供的資料來源:SQL Server 資料庫、XML 文件、ADO.NET 的DataSet、以及任何由支援 IEnumerable 或泛型 IEnumerable(Of T) 介面的物件組成的集合、 同時也規劃適用於 ADO.NET Entity Framework 的 LINQ 支援(Of T) 介面的物件組成的集合。
但每種來源型態不同,所以取得連繫的方式也不同(2)製造容器 (<collection>,翻成「集合」也行)
這個就是要製造一個「實體」,用來裝載在執行階段擷取到的資料
資料的處理方式不同,微軟設計的容器也不同:◎LINQ TO Object:以本例來說,就是用陣列當作容器,繼承自 System.Array
◎LINQ TO SQL :定義一個容器,繼承自System.Data.Linq.Table
◎LINQ TO SQL:定義一個容器,繼承自System.Collections.Generic.IEnumerable(Of System.Xml.Linq.XElement)
所以如果說是要討論「哪些物件可以查詢?」
簡單的說就是「哪些型別可以查詢?」
只要是支援了 IEnumerable(Of T)、 IQueryable(Of T) 型別的物件,都可以是查詢的對象。例如我們用陣列來當作LINQ TO Object 的練習對象,因為陣列是一種隱含的 IEnumerable(Of T)型別,所以不需要進行修改或特殊處理,就可以當成 LINQ 資料來源使用。
而 SQL Datal本身並不是IEnumerable(Of T)型別,所以需要用System.Data.Linq.DataContext 當作 LINQ TO SQL 的進入點。System.Data.Linq.Table(Of TEntity) 就是容器,支援泛型IQueryable(Of T)
XML也是一樣,XML本身並沒有實作 IEnumerable(Of T),所以要多一道手續,需要有 LINQ 提供者來實作該資料來源的「標準查詢運算子」(Standard Query Operator) 功能,將 XML 文件載入至支援泛型IQueryable(Of T)的型別:XElement。
「Step3:查詢執行階段 Query execution」就是自由發揮的時間了。
For Each…Next 就是把查詢結果「逐一」的用另一種容器(render到頁面的控制項) 給裝起來,像範例中,簡單就用字串,但甚至GridView這種可資料繫結的控制項。
也因為查詢執行與查詢建立是分開的,所以可以不用 「立即執行」(Immediate Execution),可以設計不同的觸發 (Trigger)機制來「延後執行」(Deferred Execution)
偶有所得
今天再一次看一些LINQ的內容,不知道微軟為什麼在簡介中要特別寫這一段「在以前的查詢技術,既不會在編譯時期進行型別檢查,也不支援 IntelliSense」,這句話有點怪…
「編譯時期」的型別檢查…是指偵錯功能嗎?我在「LINQ TO XML」的範例,故意把某個element的型別從string改成Integer…沒辦法「立即除錯」吧…只有開偵錯功能(快鍵F5) 才會看到整個網頁的intellitrace並顯示錯誤…(殘念的是,能力不足所以有時候真是給他看不懂。)
其實我都是寫完了用「在瀏覽器檢視(Ctrl+Shief+W)」或是「啟動但不偵錯(Ctrl+F5)」,二個的報錯是一樣的…差別是「偵錯(F5)」會提供一些疑難排解讓你連到線上或是本機上的MSDN讓你看看為什麼,可以學到不一樣的東西就是了。
還是微軟說的檢查,是把滑鼠移到某個內容上,會有標籤供「手動檢查」…所以…我覺得用這點說服人家用 LINQ 不大厚道,不過不講型別檢查這件事,使用LINQ能同時能和Visual Studio的編碼問題指示器(Coding Problem Indicators) 沾得上邊,光是這個功能我覺得就夠本了。例如我在寫這次的DEMO的時候發生了一些白痴事,如下圖:藍色底線的地方表示語意錯誤,滑鼠移過去就會顯示錯誤…除錯很方便的。
上面的問題,在於我沒有把dbconnection給實體化(entity),加個NEW讓他重新活過來就好了。離題了…再者針對SQL Server的資料查詢,不管是「偵錯」還是「IntelliSense」,在Visual Studio裡頭也是有的(不過限定Pro以上版本,express是沒有),這個SQL工具對於寫基本查詢甚至是預存程序都有點幫助,如下圖,但缺點就是不夠sense,IntelliSense只能幫你CALL出資料表來,陳述式的指令是沒有的。再來就是你要另開一個「*.sql」的查詢視窗(看檔名就知道,還是sql sever東西,只不過視窗開在Visual Studio上而已,在旁邊測試好後再放回想放的程式中,有時候我們寫事件常式,是用字串串接的方式,那個時候偵錯就很痛苦了。
所以就LINQ TO MS SQL來說,以前的工具不是功能不足,只不過我們要另外開個視窗,然後用他自已的查詢技術來除錯。XML也是一樣,Visual Studio上也有提供XMLDataSource給你用XPath和XQuery來查詢,但為了做出匹配xpath的xslt檔案,我們就要另外學另一種語法結構。
雖然我目前最常使用的是SQL SERVER,光仰賴SqlDataSource控制項為ADO.NET和DataBinding所帶來的便利性,對我來說已經很夠用 ( P.S.碰到關聯式的資料表…只會控制項的話會很累就是了),但是…
如果一切物件的查詢都能在同一個頁面上實現,然後用同一種查詢語法,然後能使用Visual Studio 現有的資源…加上VB(或C#)來加持運算…那麼我想不出什麼好理由要拒絕學習。像這次我寫LINQ TO XML,我真的開心的快瘋了,叫資料、查資料都很快。
所以基於這個論點,我想我會繼續學習LINQ。
不過我也覺得LINQ凌駕太多資料來源了,有點難以駕馭,如果你完全不懂XML,一知半解的情況下硬要LINQ 的,我覺得可能性不大,換句話說,基本的內容還是要學習,LINQ是工具的工具,了解根本LINQ才能用得好。
關於 LINQ TO SQL
這次寫的LINQ TO SQL範例,其實是純手工製造的,目的是要了解LINQ查詢database的整個運作過程,所以感覺複雜很多,從裡頭我們看得出來為了實作查詢,微軟又開發linq的組件,裡頭有很多新方法,不過實際上,Visual Studio中是有提供精靈工具來讓人使用的。這個工具就是 O/R Designer。
像我上面寫的範例,資料欄位只有二個,如果資料欄位有二十個呢,純手寫的光是對應資料欄位名稱和定義它的型別我就掛了吧。
所以這次的範例寫完,我故意用了分頁功能,切換頁面時果然出錯…唉…精靈真的幫我們做太多事了,我覺得要頒微軟MVP,最實至名歸的,應該是「精靈」
其他
把demo 上傳的時候發生錯誤「Server Error in ‘/' Application…」
底下的Compiler Error Messge:Type System.Data.Linq.DataContext is not defined
很明顯的,是我上傳網頁,但沒有對沒有註冊System.Data.Linq.dll這個組件。
註冊在哪裡哩?當然是在web.config,
所以只要把我測試的網站,把web.cnfig換掉就好。呵呵,真的是證明我是第一次用LINQ寫東西。
參考資料:LINQ (Language-Integrated Query)
It’s my life live love …
今天兒子和女兒要求要畫圖…
女兒在連哄帶騙的情況下,畫了DNowMa的最愛…小熊維尼
兒子就很麻煩了
一下要畫這個、一下要畫那個
好不容易決定要畫老鷹,然後突然…話鋒一轉
他老兄竟然要給我畫老鷹的骨頭…
沒有留言:
張貼留言