使用OData高效構建后台服務
如題本文是要說OData的,無論了解還是不了解都可以看下,本文的前半段無論是做NET還是JAVA或者其他的朋友都同樣適用,不過還是以NET為樣例說明,后半段就有點晦澀看大家心情了。
開發后台的問題
這里要先稱述下后台服務的發展歷程,從WebService的出現后到現在出現了很多后台服務相關的概念及框架概念有服務化、Restful、微服務等等,NET中的框架例如WCF、WCF數據服務、WCF Rest、MVC最后到WebAPI。
到目前為止,無論出現了多少概念還是框架,大家都還是做着查詢及更新這兩件事(對於基於數據庫的系統而言)例如:
- 請求數據 -> 后台服務 -> 查詢數據庫 -> 返回結果
- 提交數據 -> 后台服務 -> 提交數據庫 -> 返回結果
各種各樣的服務歸根結底都在處理若干這樣的請求,然而問題來了,同樣的事情必然導致了很多重復性勞動,這里我們拿一個銷售系統為例來說明,比如系統中有一組客戶數據前端有如下需求:
- 維護客戶界面,用戶可以瀏覽並編輯自己創建的客戶。
- 編輯客戶界面,用戶可以看到當前選中的客戶所有信息。
- 選擇客戶界面,用戶可以看到系統中所有客戶
- 在訂單界面,用戶可以從當前客戶的收貨地址中選擇一個地址
- 以上所有列表界面都需要分頁數據
- 以上所有查詢客戶都需要可以按名稱、代碼、地址等等10來個屬性來過濾
- 可能你的系統比較大還需要外部系統同步數據
這么多需求對於后台服務而言實際只是為了查詢和提交客戶這張數據表而已,但是這確實是每一個系統都普遍存在的需求非常之多,當然你會因為系統的需求增長,對於類似像客戶這樣的數據查詢及提交需求會層出不窮。
這里我們NET的WebApi為框架列舉下常規的做法:
- 每種查詢都定義一個API方法,這樣做有點機械不過也是最為簡單實用而且不會影響到以前的代碼。
- 有點架構思想的方法就是會定義一個公共的API方法傳入一個字典表示查詢的參數,這樣可以在服務端動態拼SQL或Lambda表達式
- 之前我有想過將LINQ序列化,然后再到服務端反序列化用於生成查詢,確實是有幾種LINQ序列化的類庫。
以上是我能想的比較常規的做法,當然可能還會有更好的做法大家可以一起討論。當然隨着項目的越來越大,各種方法或者拼接SQL的相關代碼會變的多起來,多到會讓人覺的很枯燥,這種代碼如果放在一起會有非常高度的相似性。當然解決這個問題就是本文要說的OData。
簡化的數據服務OData
上面說了這么多,其實只是為了想表達在普通業務系統中我們會有很多重復性的拼SQL或者是Lambda表達式的工作,如果我們歸納一下會發現,大多數情況服務端只需要約束用戶可操作的數據范圍即可,對於數據的操作放在前端做更為貼切一點。還是以上面這個例子來說:
- 查詢數據,無論我們是用A條件去查還是用B條件去查客戶,這都是為了前端呈現所需要的,對於服務端其實我們只要限制用戶查詢客戶的最大范圍就可以了,如果能在前端構建查詢條件然后服務端自動執行這樣開發就很高效
- 提交數據,也是同樣的道理只要在一個地方限制用戶可提交的范圍,客戶端自行組裝提交的內容即可。
如果能按上面兩條去構建服務端,這樣會大大簡化服務端。縱觀各種各樣的概念還有框架,最后我在Odata身上找到了結果,就像官網說的 OData - the Best Way to REST(最好的REST服務實現方式),目前最新協議版本4.0.1。
OData可以直接使用URL構建過濾、分頁、分組、展開相關數據等數據操作,例如下面這個官方例子:
- all products with the Name 'Milk' that also have a Price less than 2.55:
http://host/service/Products?$filter=Name eq 'Milk' and Price lt 2.55
- all categories and for each category the references of all related premier products with a current promotion equal to null
http://host/service/Categories?
$expand=Products/Sales.PremierProduct/$ref($filter=CurrentPromotion eq null)
有興趣的朋友可以去官網看下URL協議全文。
OData Net 實現
上面說的只是OData協議,不過各個語言都有了對OData的實現,這里我們只討論NET,在NET的實現有若干種,對於服務端實現其實只有一種就是ASP.NET Web API OData,這也是目前實現最好的,如果你再結合Entity Framework真正做到上文所說的客戶端通過URL可以直接構建查詢,服務端不用做任何多余代碼。開發起來非常高效、快捷,當然代碼越少出Bug的幾率也越少。
萬惡的 EDM
看標題就知道我是想吐槽它了,本來 EDM 是從Entity Framework發展出來的,這個設計的初衷是非常好的,大家想下做為一個ORM必然需要一個數據模型來記錄與數據庫對象結構的映射關系,然而 EDM 很好的在這里發揮了作用。做為一個良性的開發需要明確模型,項目越大明確數據模型就越重要。不過凡事都不能決對化,然而EF與WebApi OData都是依賴EDM,它們都不會接受EDM中不存在的數據結構、方法,而且EDM模型一旦被構建就不能再修改。這里舉個例子來說明下:
上面說所有OData協議再強大也還是有些應用場景需要自定義方法來解決,在WebApi OData中是可以構建自定義方法,如果你想傳入一個復雜的變量,就必須要在EDM中注冊這個類型,返回結果也是一樣,這樣也就表明你不可能返回多種類型的數據,也不可能返回匿名數據,還有提交數據你也不可能用DTO對象來提交了,本來WebAPI是一個很靈活的框架,但是一旦應用到WebApi OData中就變的不靈活了。
Web API OData 的缺點
使用OData也有很長時間了,發現了下面幾個缺點分享給大家:
- 上面提到的EDM問題。
- 在這個框架中你一定要用EF或者EFCore(理論上,目前我還沒實驗過,因為目前在NET Core下的版本才剛剛算穩定)才能實現URL自動轉SQL的工作,否則還是要自己手寫,而且解析這個結構也是非常難度的工作。
- 我們可以在服務端過濾用戶不能查詢的數據行,不過數據列就沒辦法過濾。
- 有時不想某些客戶端排序或過濾數據也是不可行的,在Web API OData中這些定義必須寫死在代碼中。
- 對於數據提交而言Web API OData中一次只能提交一條數據,如果有上萬條就杯具了
- 在Web API OData中是可以批量提交請求的,不過你不能批量查詢數據(我很好奇為什么)。
- 在Web API OData中批量提交最多只能提交大約2000多個子請求(每個子請求也只能提交一條數據),這里的事務提交是要自己實現的,框架中沒有提供,實現這個功能還是相當復雜的(新手不適合)。
后記
原來看過一遍文章寫開源是為了避免重復造輪子,現在看來這樣看情況了,不是每個開源的框架都是那么完美,其實不完美也不重要,重要是能快速發展。相信有不少人都有這樣的體會,之前的EntityFramwork6就是這樣,更新太慢后面直接不更新了,所以才自己開發了一個更實用的ORM Mego 。
現在Web API OData也是如此,估計短期內也解決不了上面這些問題,所以我又再一次造輪子,首先需要構造OData協議的文法,我會在下一遍博客中給大家分享。
以上是我的個人見解,僅供參考。