這篇將帶讀者了解Dapper如何在底層利用CommandBehavior優化查詢效率,如何選擇正確Behavior在特定時機。
我這邊整理了各方法對應的Behavior表格 :
方法 | Behavior |
---|---|
Query | CommandBehavior.SequentialAccess & CommandBehavior.SingleResult |
QueryFirst | CommandBehavior.SequentialAccess & CommandBehavior.SingleResult & CommandBehavior.SingleRow |
QueryFirstOrDefault | CommandBehavior.SequentialAccess & CommandBehavior.SingleResult & CommandBehavior.SingleRow |
QuerySingle | CommandBehavior.SingleResult & CommandBehavior.SequentialAccess |
QuerySingleOrDefault | CommandBehavior.SingleResult & CommandBehavior.SequentialAccess |
QueryMultiple | CommandBehavior.SequentialAccess |
首先可以看到每個方法都使用CommandBehavior.SequentialAccess
,該標籤主要功能 使DataReader順序讀取行和列,行和列不緩衝,讀取一列後,它會從內存中刪除。
,有以下優點 :
避免二進制大資源一次性讀取到記憶體
,尤其是Blob或是Clob會配合GetBytes 或 GetChars 方法限制緩衝區大小,微軟官方也特別標註注意 :加快查詢效率
但它卻不是
DataReader的預設行為,系統預設是CommandBehavior.Default
CommandBehavior.Default有著以下特性 :
多個
結果集(Multi Result)這兩個特性跟生產環境情況差滿多,畢竟大多時刻是只需要一組結果集配合有限的記憶體
,所以除了SequentialAccess外Dapper還特別在大多方法使用了CommandBehavior.SingleResult
,滿足只需一組結果就好避免浪費資源。
這段還有一段細節的處理,查看源碼可以發現除了標記SingleResult外,Dapper還特別加上一段代碼在結尾while (reader.NextResult()){}
,而不是直接Return(如圖片)
早些前我有特別發Issue(連結#1210)詢問過作者,這邊是回答 : 主要避免忽略錯誤,像是在DataReader提早關閉情況
有時候我們會遇到select top 1
知道只會讀取一行資料的情況,這時候可以使用QueryFirst
。它使用CommandBehavior.SingleRow
可以避免浪費資源只讀取一行資料。
另外可以發現此段除了while (reader.NextResult()){}
外還有while (reader.Read()) {}
,同樣是避免忽略錯誤,這是一些公司自行土炮ORM會忽略的地方。
兩者差別在QuerySingle沒有使用CommandBehavior.SingleRow,至於為何沒有使用,是因為需要有多行資料才能判斷是否不符合條件並拋出Exception告知使用者
。
這段有一個特別好玩小技巧可以學,錯誤處理直接沿用對應LINQ的Exception,舉例:超過一行資料錯誤,使用new int[2].Single()
,這樣不用另外維護Exceptiono類別,還可以擁有i18N多國語言化。