DbUtility v3
歷史
七年前,也就是2007年,我在博客園寫了一篇博文,開源並發布了恐怕是我第一個開源項目,DbUtility。其設計的初衷就是為了簡化ADO.NET繁瑣的數據庫訪問過程,提供極為簡潔流暢的語法訪問數據庫,像這樣:
dbUtility.ExecuteSingleRow("SELECT username, userdata FROM Users WHERE ID = {0}", userId );
這一行代碼將自動創建和打開數據庫鏈接、創建命令對象、包裝參數、執行查詢並將結果包裝成DataRow返回。
時過境遷,七年后的現在,我們有了非常多的重量級的ORM如NHibernate、EntityFramework,和各種快速訪問數據庫的框架。但有些時候,我們仍然會希望,執行一個簡單的SQL查詢,並獲得其結果。
這時候我經常會想起DbUtility這個家伙,所以在七年后的今天,我把DbUtility的所有代碼重新寫了一次,推出了這個超輕量級數據庫訪問幫助器的最新版本,DbUtility v3,目前在GitHub上開源。
簡單查詢
如果你從來沒有聽說過這個東西,也沒有關系,DbUtility是被設計為超輕量級(無需任何額外的配置,不會產生任何未知的行為),隨手可用(語法簡單、直接、明了)的數據庫訪問幫助器。
一個典型的DbUtility v3的代碼像是下面這樣:
db.T( "SELECT Username FROM Users WHERE ID = {0};", userId ).ExecuteScalar<string>();
幾乎每個數據庫的查詢差不多都是這種形式,其中可以分為三個部分:
查詢執行器,即上面代碼中的 db ,這個東西負責執行查詢,其與數據庫直接相關,一般我們可以使用一個連接字符串配置或者連接字符串來創建查詢執行器的實例:
var db = new SqlDbUtility( "數據庫連接字符串" ); var db = SqlDbUtility.Create( "連接字符串名稱" );
緊跟着查詢執行器的部分是查詢構建器,這一部分決定了數據庫具體要執行的查詢。也就是上面代碼中的 .T( "SELECT Username FROM Users WHERE ID = {0};", userId ) 部分,T的意思是Template,即模板,這也是DbUtility最基本的查詢構建方式,通過SQL指令模板來構建。我們可以使用類似於string.Format的語法指定字符串模板和模板參數。但與直接調用string.Format不同的是,這個模板中的參數會被轉換為參數化查詢中的參數,從而沒有注入式漏洞的隱患。即上述的查詢模板最終轉換成的SQL大體上是這樣的:
DECLARE @Param0 AS int = userId-value; SELECT Username FROM Users WHERE ID =@Param0;
T或者Template方法(事實上T是縮寫)會解析查詢模板並創建一個抽象的參數化查詢對象,參數化查詢對象會根據具體的數據庫產生相應的參數化查詢進行執行。
最后的一部分是結果構建器,也就是上面代碼中的 .ExecuteScalar<string>() ,ExecuteScalar即是取出查詢結果中的第一行第一列。
DbUtility v3提供了極為豐富的結果構建器,除了ExecuteScalar、ExecuteNonQuery、ExecuteDataTable、ExecuteFirstRow這些常見常用的之外,還有自動將結果包裝成實體的ExecuteEntity和ExecuteEntities,包裝成動態對象的ExecuteDynamicObject和ExecuteDynamics等等等等。豐富的結果構建器可以極大地簡化你訪問數據庫的代碼。
不過最重要的是,DbUtility v3設計了一個便於擴展的架構,所有的查詢構建器、結果構建器,全部可以簡單地自定義擴展,或者說事實上整個DbUtility提供的所有的查詢構建器和查詢結果構建器本來就是用擴展方法擴展出來的。
進階
如果你要開啟數據庫事務,使用DbUtility也非常的方便:
using( var transaction = db.BeginTransaction() ) { transaction.T( "SELECT Username FROM Users WHERE ID = {0}", userId ).ExecuteScalar<string>();//事務對象可以直接當做查詢執行器來使用。
//... transaction.Commit();//提交事務,若在離開using塊之前沒有提交事務,則事務會自動回滾。 }
異步查詢數據庫也非常的簡單:
var username = await db.T("SELECT Username FROM Users WHERE ID = {0};", userId ).ExecuteScalarAsync<string>();//加上Async后綴即是訪問異步版本。
Jumony的示例項目全部數據庫訪問改用DbUtility后,比起EntityFramework來說,不僅配置消失了,性能也更好了(因為DbUtility是超輕量級的),更重要的是,結合MVC 4的異步Action,我們可以非常簡單的在ASP.NET中異步訪問數據庫以獲得更大的吞吐量:
public async Task<ActionResult> Index() { return View( "index", await dbUtility.T( "SELECT ID, Title, Completed FROM Tasks" ).ExecuteEntitiesAsync<TodoTask>() ); }
尾聲
在馬上發布的更新中,我們可以簡單地把多個參數化查詢對象像拼接字符串一樣拼接起來:
var query = db.T( "SELECT * FROM Users" ); query += "WHERE" + Db.Join( "AND", Db.T( "Age > {0}", age ), Db.T( "FirstName LIKE '%'+{0}+'%'", name ) ); var result = query.ExecuteEntities<User>();
在未來的版本中,將支持更多的數據庫類型(如Excel),更好的查詢構建(值得期待的超輕量SQL語法: db.Q( "users.username ?users.userId = @0", userId ) )。
DbUtility v3現在已經可以通過 NuGet 獲取。
全線開源項目全部在GitHub以Apache協議開源,DbUtility:超輕量數據訪問幫助,Jumony:真正的HTML分析、處理、綁定、視圖引擎,WebTest:在ASP.NET環境直接進行單元測試,LogUtility:文本日志記錄利器。
以上項目均可以在 NuGet 下載。