2012年7月19日 星期四

LINQ三部曲…用基本查詢三步驟查詢三大資料來源

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

用張圖來補充說明
image

從上圖,配合著程式碼來看,我們知道「查詢建立」和「查詢執行」階段是分開的,「查詢建立」的時期並不會擷取任何資料

差異中最小的是「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
image

◎LINQ TO SQL :定義一個容器,繼承自System.Data.Linq.Table
image

◎LINQ TO SQL:定義一個容器,繼承自System.Collections.Generic.IEnumerable(Of System.Xml.Linq.XElement)
image

所以如果說是要討論「哪些物件可以查詢?」
簡單的說就是「哪些型別可以查詢?」
只要是支援了 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並顯示錯誤…(殘念的是,能力不足所以有時候真是給他看不懂。)
image

其實我都是寫完了用「在瀏覽器檢視(Ctrl+Shief+W)」或是「啟動但不偵錯(Ctrl+F5)」,二個的報錯是一樣的…差別是「偵錯(F5)」會提供一些疑難排解讓你連到線上或是本機上的MSDN讓你看看為什麼,可以學到不一樣的東西就是了。
image

還是微軟說的檢查,是把滑鼠移到某個內容上,會有標籤供「手動檢查」…所以…我覺得用這點說服人家用 LINQ 不大厚道,不過不講型別檢查這件事,使用LINQ能同時能和Visual Studio的編碼問題指示器(Coding Problem Indicators) 沾得上邊,光是這個功能我覺得就夠本了。例如我在寫這次的DEMO的時候發生了一些白痴事,如下圖:藍色底線的地方表示語意錯誤,滑鼠移過去就會顯示錯誤…除錯很方便的。
image

上面的問題,在於我沒有把dbconnection給實體化(entity),加個NEW讓他重新活過來就好了。離題了…再者針對SQL Server的資料查詢,不管是「偵錯」還是「IntelliSense」,在Visual Studio裡頭也是有的(不過限定Pro以上版本,express是沒有),這個SQL工具對於寫基本查詢甚至是預存程序都有點幫助,如下圖,但缺點就是不夠sense,IntelliSense只能幫你CALL出資料表來,陳述式的指令是沒有的。再來就是你要另開一個「*.sql」的查詢視窗(看檔名就知道,還是sql sever東西,只不過視窗開在Visual Studio上而已,在旁邊測試好後再放回想放的程式中,有時候我們寫事件常式,是用字串串接的方式,那個時候偵錯就很痛苦了。
image 

所以就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,最實至名歸的,應該是「精靈」
image


其他

把demo 上傳的時候發生錯誤「Server Error in ‘/' Application…」

底下的Compiler Error Messge:Type System.Data.Linq.DataContext is not defined
image

很明顯的,是我上傳網頁,但沒有對沒有註冊System.Data.Linq.dll這個組件。
image

那我是怎麼寫出來的…?呵呵,我寫的時候當然是在本機上,寫的時候其實Visual Studio就已經幫我偷偷註冊這個網站要用到System.Data.Linq.dll,這個組件可以讓 LINQ 能夠與 .NET Framework 集合、SQL Server 資料庫、ADO.NET DataSet以及 XML 文件搭配使用。
註冊在哪裡哩?當然是在web.config,image

所以只要把我測試的網站,把web.cnfig換掉就好。呵呵,真的是證明我是第一次用LINQ寫東西。

參考資料:LINQ (Language-Integrated Query)


It’s my life live love …

今天兒子和女兒要求要畫圖…
女兒在連哄帶騙的情況下,畫了DNowMa的最愛…小熊維尼
兒子就很麻煩了
一下要畫這個、一下要畫那個
好不容易決定要畫老鷹,然後突然…話鋒一轉
他老兄竟然要給我畫老鷹的骨頭…
image

哥哥邊畫還邊嬉皮笑臉的說:「ya…我沒看過老鷹的骨頭欸…ya…上面的英文我看不懂欸…」
哥哥邊畫還邊嬉皮笑臉的說:「ya…我沒看過老鷹的骨頭欸…ya…上面的英文我看不懂欸…」
叫妹妹拍照,她說她很忙,妹妹很努力的收拾筆 (應該是為了蓋印章啦)
叫妹妹拍照,她說她很忙,妹妹很努力的收拾筆 (應該是為了蓋印章啦)

沒有留言:

張貼留言

Related Posts Plugin for WordPress, Blogger...
// Dnow Function