本來閒來無事,準備看看Dapper擴充套件的原始碼學習學習其中的程式設計思想,同時整理一下自己程式碼的單元測試,為以後的進一步改進打下基礎。
突然就發現問題了,原始碼也不看了,開始改程式碼,改了好久。
測試Dapper.LiteSql資料批次插入的時候,耗時20秒,感覺不正常,於是我測試了非Dapper版的LiteSql的批次插入,僅需100毫秒,速度差了200倍。
同樣的資料庫、同樣的Npgsql.dll、同樣的測試程式碼,產生的SQL和引數集合也是一樣的,最後不得不懷疑Dapper。
參照Dapper的原始碼,修改偵錯之後,我決定提個PR。我之前沒想過提PR,我想我也不是為了提PR而提PR,我也不想費時費力。
沒想到提PR的過程很不順利,原來提的PR需要單元測試全部通過才行。
我本機沒環境不方便測,不過我發現提交申請之後,GitHub上立馬自動編譯測試,能看到哪些單元測試對了,哪些錯了,真強大,真方便,原來大工程是這樣的。
先是提交了一行程式碼,認為沒有問題,結果被打了個叉。仔細一看才發現,原來是單元測試不通過。
改了又改,有幾個儲存過程相關的單元測試總是不通過。
都快要放棄了,後來想到是不是我定義的cleanNames變數隨著DynamicParameters類的建立,又清空了,但cleanNames又不能定義成全域性的。後來我加了幾行程式碼。
一共提交了12次,單元測試終於全部通過,共增加了11行程式碼。
PR是提交了,是否被採納就不知道了。也許程式碼寫的比較挫,也許稽核人員不認可我對Dapper的這種用法,是我用錯了,沒有修改的必要。
變數定義:
private readonly HashSet<string> cleanNames = new HashSet<string>();
單元測試始終無法全部通過,我新增了下面幾行程式碼,終於成功了。原來cleanNames被重新new了之後,command.Parameters裡可能是有值的,它的作用域更大一些。
foreach (IDbDataParameter param in command.Parameters)
{
if (!cleanNames.Contains(param.ParameterName))
{
cleanNames.Add(param.ParameterName);
}
}
關鍵的修改就一行
原始碼(引數很多時效能不行,引數什麼情況下會很多呢?就是通過一條SQL批次Insert時會有很多引數,我是500條資料插入一批,引數根據表欄位多少可能有幾千個):
bool add = !command.Parameters.Contains(name);
當command.Parameters中有幾千個引數的時候,效能就慘不忍睹了。
集合查詢的時間複雜度是O(N)。(原來我寫的是O(N/2),現更正為O(N))
修改為(HashSet效能很高):
bool add = !cleanNames.Contains(name);
通過HashSet查詢,時間複雜度是O(1)。
往cleanNames中新增欄位名:
if (add)
{
command.Parameters.Add(p);
cleanNames.Add(name);
}
關於List集合的Contains方法
當你使用Contains方法的時候,你要考慮這個集合有沒有可能突然變的資料量很大?如果是在迴圈中頻繁呼叫,並且List的資料量比較大,它的效能就比較差,建議使用HashSet或Dictionary來判查詢。
但是HashSet、Dictionary和List轉來轉去也有代價,IDbCommand介面的Parameters屬性的型別是IDataParameterCollection,它是一個集合,並沒有HashSet或Dictionary型別的屬性,又必須要轉換才能得到。
issue:https://github.com/DapperLib/Dapper/issues/1817
PR:https://github.com/DapperLib/Dapper/pull/1816
LiteSql源於DBHelper,裡面的介面是做過實際專案的,主要是ERP、CRM系統。
簡單支援了Lambda表示式、增加了SqlString之後,使用上似乎變複雜了一點,不過原來的使用方式依然支援。
也許這裡面的介面和設計思想,體現的是我上家公司的前輩們的技術水準。比如實體類用partial修飾分成兩個檔案,可能有利有弊吧。自動生成的Model類是不建議修改的,否則資料庫變動的時候你還怎麼自動生成?不把你的改動沖掉了?
一個ORM有它的設計思想和理念,比如DapperExtensions就不建議對實體類加特性,而是通過獨立的對映類來處理表、欄位別名,優缺點我還不清楚。
LiteSql的後續改進,還沒有新的指導思想,所以一直都是小改,基本沒怎麼動。
https://gitee.com/s0611163/Dapper.LiteSql
https://gitee.com/s0611163/LiteSql
即使是大名鼎鼎的Dapper我依然不放心,所以保留了ADO.NET的版本。