DbUtility v3 背后的故事
時間
-
DbUtility v3構思了差不多大半年,真正開發到第一個版本發布到NuGet卻只花了50天。中途大量時間在完善 Jumony 3,只有三周來開發DbUtility v3,其中總工時大概不超過20個小時。
-
DbUtility項目開始於2003年,2005年增加了實體轉換的支持,2007年因為LINQ的發布,認為這個項目將來不會再有更新,遂開源,也是我開源的第一個項目,當時托管於Codeplex。
-
每當我要快速搞定一個事情的時候,DbUtility總是最稱手的工具,事實上在2007年之后DbUtility有四年沒有任何修改,因為我覺得沒有什么新的功能需要。
-
如果不是眼饞.NET Framework 4.5新增的異步支持以及越來越多需要定制化查詢結果(例如包裝成Dictionary)的需求,DbUtility可能到現在都不會重寫。
設計
-
DbUtility v3花了很長的時間來構思他的API設計,希望既能兼容之前的形式,保持簡潔,流暢的特點,又利於擴展。下了很大的決心才最終敲定db.T( xxx ).ExecuteXXX()這樣的形式,這與之前是不兼容的,所以新發布了一個NuGet包,避免原來的項目升級大面積的編譯錯誤。
-
利用擴展方法實現的API具有可以自行擴展以及整體切換的特點。這一風格是Jumony首創的,DbUtility把這個手法運用的更為嫻熟了。
-
同樣花了很長的時間才敲定同時提供T和Template兩個一模一樣的方法,事實上T是Template的縮寫。雖然具備智能提示可以非常便捷的輸入Template,但我仍然認為,T這樣的縮寫,可以最大限度減少代碼的行寬,不帶來額外的噪聲污染。
-
db.T( xxx )將不會執行查詢,必須使用db.T( xxx ).ExecuteNonQuery()一直是整個設計中的一大痛處,因為可能會忘記寫.ExecuteNonQuery。但是的確沒有辦法解決。
-
DbUtility的結果構建器,即ExecuteXXX方法,原本為了簡潔是打算去掉Execute前綴的,例如:db.T( “SELECT * FROM Users” ).ExecuteEntities()原本是打算寫成db.T( “SELECT * FROM Users” ).Entities()。但是由於考慮到某些查詢構建器(即T(xxx))可能難以在一個方法內寫完,需要多次調用構建最終的查詢,此時執行查詢的方法和構建查詢的方法將沒有明顯的區分,故而依然保留Execute這個前綴。
- 譬如說
db.Table( "Users" ).Fields( "*" ).ExecuteEntities<User>()
。
- 譬如說
-
得益於新的架構設計,Jumony的結果構建器擴展方法寫起來非常簡單,所以我一口氣提供了十幾個用於構建不同類型結果的擴展方法。
技術
-
DbUtility擁有理論上性能最好的實體轉換器,其原理是根據實體類型直接Emit一個轉換器方法出來進行實體轉換,而Emit出來的這個方法則巧妙地利用類型字典被緩存。這兩項技術都是.NET的不傳之秘,較之不使用這兩項技術的實現方式性能不在一個數量級。
-
DbUtility的API將C#的泛型和類型推斷運用到了極致,事實上諸如T這樣的擴展方法,接受的第一個參數的類型是IDbExecutor,而SqlDbUtiltiy類型恰好實現了這個接口,所以可以調用。也就是說如果哪個數據庫的查詢器不支持參數化查詢(沒有實現這個借口),那么就點不出T出來。更神奇的是如果數據庫查詢器不支持異步查詢,那么所有的異步API也會自動消失。
-
DbUtility的異步API實現貫徹了許多可以異步的地方,如打開數據庫連接,執行查詢。目前只剩下DataReader.Read方法沒有調用異步版本,這將在后面的更新中解決。DbUtility恐怕也是第一個實現異步API的數據庫訪問幫助器。
-
Java移植項目在充分利用C#語法特性上簡直令人發指,各種莫名其妙不符合.NET命名規則的API搞得各種烏煙瘴氣。而DbUtility不僅僅充分利用了泛型、擴展方法一系列特性,更提供了運算符重載,可以非常直觀的把參數化查詢像拼接字符串一樣拼接起來。
-
在開發DbUtility v3的時候的確考慮過推出一個面向.NET Framework 3.5的版本,但是因為要同時維護兩個版本過於費力。且DbUtility並非我的重心,故而作罷。但事實上刪除DbUtility內所有的異步API,便可以得到在3.5下編譯通過的代碼。
優勢
很多人會問我,DbUtility的優勢在哪里?
事實上我一點兒都不想討論這個問題,從根本上來說,DbUtility僅僅只是因為用了太多年非常順手所以改進了一下讓其與時俱進而已。
由於是超輕量級的數據庫訪問框架,代碼量甚至都不過千行,可以說大家都是一樣的,半斤八兩沒有區別。除非哪個不用Emit來做實體轉換(這種垃圾應該趕緊扔掉)會造成性能下降。
但是總的來說,DbUtility仍然有一些特有的東西是其他輕量級框架難以企及的:
- 異步數據庫查詢
- DbUtility v3率先推出了異步數據庫查詢實現,並將在后面不斷完善。
- 可擴展的API
- DbUtility v3采用了和Jumony項目一樣的可擴展API設計,任何人任何第三方都可以在不修改現有代碼之下對現有API進行擴展。
- 可替換和自定義的API
- 不僅僅是可以擴展現有API,甚至可以把整個API給替換掉。簡單來說,如果你喜歡,甚至可以把DbUtility的API給換成Dapper一模一樣的,但最終還是以DbUtility的核心在驅動。
- 參數化查詢抽象
- 在v3的設計中,增加了抽象的參數化查詢對象,參數化查詢對象是與數據庫無關的,在支持參數化查詢的數據庫,將會嘗試采用參數化查詢。而在不支持參數化查詢的數據庫,則嘗試將參數值進行相應處理,以避免注入式問題。
- 結構化查詢抽象
- 在將來,DbUtility 還將增加結構化查詢抽象,將SQL查詢抽象為語法樹,通過方法來構建。在執行時再根據實際的數據庫轉換為相應的語法查詢。
- 查詢執行器、查詢、結果構建器分離架構
- 吸取之前的經驗,新的架構可以使得三部分獨立的擴展,和諧的統一。
事實上DbUtility是一個面向真正程序員的超輕量數據訪問框架,其擁有極大的可擴展性,並且擴展起來極為簡便。這是DbUtility相較於其他超輕量數據訪問框架的最大優勢。
未來
DbUtility 即將到來的功能包括:
- 更豐富的配置項
- 例如不要總是異步打開連接(因為連接池內很可能已經存在連接)
- 查詢超時時間
- 更好的異步支持(異步的Read調用)
- 更好的存儲過程支持
- 查詢日志記錄和查詢事件追蹤(例如執行了哪些查詢,執行了多久)。
在未來的版本中將會添加:
- 更多的數據庫支持(譬如Excel?!)
- 結構化的 SQL 查詢構建工具
- 自定義類型映射(例如XML字段映射到XDocument)
以及更多,,,,
參與
DbUtility是一個開源項目,其足夠輕便和簡單,所以參與到DbUtility的開發過程中也是非常簡單的,您可以通過以下的方式來參與:
- 提交Bug和功能需求,最好附帶測試用例
- fork項目,並增加測試用例
- fork項目,並增加新的功能
- 在實際項目中使用他,這樣能更好的發現問題。