小喵在一次去 TWMVC 的場合上課中,聽到 KKBruce 講解有關Web API的內容,這裡面提到了OData這個東西,感覺還蠻有趣的,後來 KKBruce 大大也來小喵的部落格留言,也提到了 OData 。小喵開始找一下相關的資料,發現 OData 搭配 WebAPI 有蠻多不錯的運用。於是,就來場小喵與OData的初體驗吧~
緣起
小喵在一次去 TWMVC 的場合上課中,聽到 KKBruce 講解有關Web API的內容,這裡面提到了OData這個東西,感覺還蠻有趣的,後來 KKBruce 大大也來小喵的部落格留言,也提到了 OData 。小喵開始找一下相關的資料,發現 OData 搭配 WebAPI 有蠻多不錯的運用。於是,就來場小喵與OData的初體驗吧~
準備
首先小喵先把基本測試測試的東西建立起來,資料使用北風資料庫中的 Products 來進行測試,暫時先不透過 Entity Framework 或者工具建立物件,暫時以手工打造的方式來做體驗。
北風Product的類別,小喵就暫時先在Model中,新增了ProductInfo的類別
Public Class ProductInfo
Private m_ProductID As Decimal
Private m_ProductName As String = ""
Private m_SupplierID As Decimal
Private m_CategoryID As Decimal
Private m_QuantityPerUnit As String = ""
Private m_UnitPrice As Decimal
Private m_UnitsInStock As Decimal
Private m_UnitsOnOrder As Decimal
Private m_ReorderLevel As Decimal
Private m_Discontinued As Boolean = False
Public Property ProductID As Decimal
Get
Return m_ProductID
End Get
Set(value As Decimal)
m_ProductID = value
End Set
End Property
Public Property ProductName As String
Get
Return m_ProductName
End Get
Set(value As String)
m_ProductName = value
End Set
End Property
Public Property SupplierID As Decimal
Get
Return m_SupplierID
End Get
Set(value As Decimal)
m_SupplierID = value
End Set
End Property
Public Property CategoryID As Decimal
Get
Return m_CategoryID
End Get
Set(value As Decimal)
m_CategoryID = value
End Set
End Property
Public Property QuantityPerUnit As String
Get
Return m_QuantityPerUnit
End Get
Set(value As String)
m_QuantityPerUnit = value
End Set
End Property
Public Property UnitPrice As Decimal
Get
Return m_UnitPrice
End Get
Set(value As Decimal)
m_UnitPrice = value
End Set
End Property
Public Property UnitsInStock As Decimal
Get
Return m_UnitsInStock
End Get
Set(value As Decimal)
m_UnitsInStock = value
End Set
End Property
Public Property UnitsOnOrder As Decimal
Get
Return m_UnitsOnOrder
End Get
Set(value As Decimal)
m_UnitsOnOrder = value
End Set
End Property
Public Property ReorderLevel As Decimal
Get
Return m_ReorderLevel
End Get
Set(value As Decimal)
m_ReorderLevel = value
End Set
End Property
Public Property Discontinued As Boolean
Get
Return m_Discontinued
End Get
Set(value As Boolean)
m_Discontinued = value
End Set
End Property
End Class
另外寫個 ProductDAO ,用來寫資料存取的部分,先寫取得全部資料的一個 Public Function 用來讀出所有的資料,並且傳回List (Of ProductInfo)
Public Function GetAllProducts() As List(Of ProductInfo)
Try
Dim oConnStr As New ConnStrInfo
Dim ConnStr As String = oConnStr.ConnStr
Using Conn As New SqlConnection(ConnStr)
Conn.Open()
Dim oProds As New List(Of ProductInfo)
Dim tProd As ProductInfo
Dim SqlTxt As String = ""
SqlTxt &= " SELECT ProductID "
SqlTxt &= " FROM Products (NOLOCK) "
SqlTxt &= " "
Using Cmmd As New SqlCommand(SqlTxt, Conn)
Dim Dr As SqlDataReader = Cmmd.ExecuteReader
If Dr.HasRows Then
While Dr.Read
tProd = New ProductInfo
tProd.ProductID = Dr.Item("ProductID")
tProd.ProductName = Dr.Item("ProductName")
tProd.SupplierID = Dr.Item("SupplierID")
tProd.CategoryID = Dr.Item("CategoryID")
tProd.QuantityPerUnit = Dr.Item("QuantityPerUnit")
tProd.UnitPrice = Dr.Item("UnitPrice")
tProd.UnitsInStock = Dr.Item("UnitsInStock")
tProd.UnitsOnOrder = Dr.Item("UnitsOnOrder")
tProd.ReorderLevel = Dr.Item("ReorderLevel")
tProd.Discontinued = Dr.Item("Discontinued")
oProds.Add(tProd)
End While
End If
Dr.Close()
End Using
Return oProds
End Using
Catch ex As Exception
Throw New Exception(ex.Message)
End Try
End Function
最後,再準備可以傳回全部 Products 的 Controller
' GET api/products
Public Function GetValues() As List(Of ProductInfo)
'Return New String() {"value1", "value2"}
Dim ProdObj As New ProductDAO
Dim oProds As List(Of ProductInfo) = ProdObj.GetAllProducts()
Return oProds
End Function
這樣準備好,就可以開始測試看看,可以傳回全部的Products的資料了
OData 變身~
要將本來輸出為List(Of Object)轉換為輸出可支援OData的內容,其實只要小小的三個步驟
- 修飾本來的Function,設定他變成可以查詢【 Queryable 】
- 本來傳回的 List(Of 改為【 iQueryable(Of 】
- 回傳的部分,加上【 .AsQueryable 】
是的,就是這樣小小的三步驟,就可以把他變成 OData 。(灑花)
<Queryable>
Public Function GetValues() As IQueryable(Of ProductInfo)
Dim ProdObj As New ProductDAO
Dim oProds As List(Of ProductInfo) = ProdObj.GetAllProducts()
Return oProds.AsQueryable
End Function
OData 指令
在開始試用之前,先來整理一下幾個常用的OData指令,以及指令的語法
Uri規則:
首先必須在本來的Uri之後加上個【?】符號
接著,在每個指令之前,都必須加上一個【$】符號。(這個會不知不覺忘記,所以如果出現的結果不是預期的,可以先檢查是否漏了這個)
指令與指令連接,這部分與之前的QueryString的變數連接一樣,要用【&】符號
指令:
指令 | 說明 | 範例 |
top | 結果挑出最前面的幾筆 | ?$top=3 |
skip | 略過幾筆。可用於分頁顯示 | ?$skip=10 |
orderby | 排序 | ?$orderby=SupplierID,ProductID |
filter | 篩選 | |
gt : > , 大於 | $filter=ProductID gt 10 | |
lt : < , 小於 | $filter=ProductID lt 10 | |
ge : >=, 大於等於 | $filter=ProductID ge 10 | |
le : <=, 小於等於 | $filter=ProductID le 10 | |
eq : =, 等於 | $filter=ProductID eq 10 | |
ne : <>, 不等於 | $filter=ProductID ne 10 |
其他的指令,請參考以下官方的說明
http://msdn.microsoft.com/en-us/library/windowsazure/gg312156.aspx
試用
接著懷著興奮的心情,就來體驗看看是否能夠直接在Uri設定指令,不改程式的狀況就可以有一些篩選、TOP、排序、略過的功能
首先是直接執行,OK沒問題
TOP n :選出最先的10筆
發現排序上就不是以ProductID來排
Top 10 加上依照ProductID來排序
Uri : http://localhost:2639/api/Products?$top=10&$orderby=ProductID
以ProductID排序 + 略過25筆 + 顯示5筆。
使用情境:以ProductID排序,分頁,一頁顯示5筆,在第6頁
以UnitPrice倒序排序,選出前5筆
使用情境:顯示最貴的5筆資料
進一步看OData
從以上的測試,可以在不修改程式的前提下,透過Uri的指令,就可以對查詢的資料進行排序、篩選、top、略過等等的功能,可以在最小的開發成本,獲得相當大的能力。真是不錯。
而小喵的測試,是使用自訂的類別,用的是List<T>的方式,轉成IQueryable<T>,其實資料還是將全部撈取回來後,放在記憶體中,然後再針對記憶體中的資料進行相關的篩選或排序。小喵從黑大的文章裡面得知,其實如果搭配的是Entity Framework或者LINQ to SQL這類的ORM物件的話,以篩選來說,會真正的產生WHERE的SQL語法去對資料庫篩選,這樣記憶體的使用將會更省。相關的文章大家可以參考以下黑大這篇精彩的文章:
總結
OData 的初體驗到此進入尾聲。從這次的初體驗,小喵個人覺得 OData 的確是一個簡單易學、容易開發使用的一個機制,對於WebAPI的應用上,提供了更大的彈性,可以讓我們專注於產出相關的資料,至於資料如何應用,就交由使用端透過 Uri 來決定。
在小喵去 TWMVC 上課的過程中,KKBruce大大還展現了,可以直些將OData的網址,提供給Excel,然後就可以將資料直接在Excel中使用。
很不錯的一套機制,小喵的初體驗在此紀錄一下,也提供大家參考。
以下是簽名:
- 歡迎轉貼本站的文章,不過請在貼文主旨上加上【轉貼】,並在文章中附上本篇的超連結與站名【topcat姍舞之間的極度凝聚】,感恩大家的配合。
- 小喵大部分的文章會以小喵熟悉的語言VB.NET撰寫,如果您需要C#的Code,也許您可以試著用線上的工具進行轉換,這裡提供幾個參考
Microsoft MVP Visual Studio and Development Technologies (2005~2019/6) | topcat Blog:http://www.dotblogs.com.tw/topcat |