簡單說明Dapper 以及官方擴充Dapper.Contrib.Extensions 使用上的小測試
和ADO.Net 做一些效能測試比較
很多前輩介紹過Dapper
列出一些我參考過的文章
黑案執行緒-前輩的 短小精悍的.NET ORM神器 -- Dapper
Huan-Lin 學習筆記-前輩的 好用的微型 ORM:Dapper
另外這是 Dapper官方的GitHub 有些未來更新計畫或是討論會在裡面
也有不少東西可以參考。
先說一下 官方的擴充 Dapper.Contrib.Extensions,去他們的 GitHub可以看到一些範例
擴充方法如下
T Get<T>(id); IEnumerable<T> GetAll<T>(); int Insert<T>(T obj); int Insert<T>(Enumerable<T> list); bool Update<T>(T obj); bool Update<T>(Enumerable<T> list); bool Delete<T>(T obj); bool Delete<T>(Enumerable<T> list); bool DeleteAll<T>();
簡單來說就是 讓我們資料類別可以直接 查詢/新增/修改/刪除
但是需要設定 類別需要先設定 Special Attributes 才能做資料對應
但是條件不夠寬鬆,很多時候 使用還是自己下 Query 和 Execute比較方便
原因如下 Get 只有參考 Key Attributes 而已,無法參考其他參數,變成要 GetAll 之後再自己過濾資料
而新增 跟 修改,該Key或是新增的資料物件的值都必須填寫,無法不設定,就算Table欄位有預設值也一樣
效能測試之前。再提些其他的小測試
官方的GitHub裡面介紹的一些用法 基本上都需要
new { Age = (int?)null, Id = guid }
這樣去加入動態參數進去做對應
經過我實測結果 其實直接把
該物件類別 new 實體後,對應下的Sql指令去填資料
如下 可以改成這樣,因只需要Id 所以填寫 Id即可
而Age 因為預設值是null 所以查詢指令的時候會自動填寫預設值進去
var dogTest = new Dog(); dogTest.Id = Guid.NewGuid(); var queryDog = _connection.Query<Dog>("select Age = @Age, Id = @Id", dogTest);
此方法對新增和修改資料都有效,只要指令下達正確 不要對應不到類別屬性名稱即可
因為官方的使用方式沒寫到類似的用法,所以才想到要測試這用法
這用法有一個好處,在下一篇文章會提到。
另外稍微提一下
官方有提到一個 比較特別的使用方式
如果要使用 SQL的 Output 或 ReturnValue
需要使用DynamicParameters 按照上圖的方式去設定參數
測試結果,只能按照此方法去做設定,不能使用 物件類別 去做承接
就算回傳的參數是該類別的屬性,一樣會無效雖然可以執行
但是 資料庫參數的數值不會回傳至該物件的屬性上
比較會常用到的 就是要取得自動增加的 Id,如果本來使用Output方式去取的
就改變成在命令後面加上SELECT SCOPE_IDENTITY()
使用查詢方式去取得資料
之後再用 Dapper的 ExecuteScalar<int>。將查詢結果的Id抓出來
如果用 Stored Procedure 原理也差不多
以下是一些效能測試的比較
對應的資料類別
[Table("TestTable")] public class TestTable { [Key] public int Id { get; set; } // int public Guid Guid { get; set; } // uniqueidentifier public string Name { get; set; } // nvarchar(50) }
以下測試均跑 10次迴圈 每次都查詢 100000次
不過因為不是不同的資料,所以不保證Dapper內部沒做快取或是其他加快的方式
這部分以後有研究到在做補充
第一次測試 使用 Dapper.Contrib.Extensions 擴充的 Get。結果如下
var testData = _SqlConnect.Get<TestTable>(1);
第0次 13,863ms 第1次 12,793ms 第2次 13,443ms 第3次 13,356ms 第4次 14,810ms 第5次 14,985ms 第6次 12,894ms 第7次 12,764ms 第8次 12,822ms 第9次 13,703ms
第二次測試 使用 Dapper 擴充的 Query。結果如下
因為只需要單筆,並且已確定有資料所以就直接使用QuerySingle
string strCmd = "select * from TestTable where Id = 1"; var testData = _SqlConnect.QuerySingle<TestTable>(strCmd);
第0次 12,621ms 第1次 11,499ms 第2次 11,429ms 第3次 11,533ms 第4次 11,460ms 第5次 12,567ms 第6次 11,328ms 第7次 11,298ms 第8次 11,306ms 第9次 11,306ms
第三次測試 使用 ADO.Net 做查詢。結果如下
_SqlCmd.CommandText = "select * from TestTable where Id = 1"; var reader = _SqlCmd.ExecuteReader(); reader.Read(); int aa = Convert.ToInt32(reader[0]); reader.Close();
第0次 11,512ms 第1次 10,161ms 第2次 10,417ms 第3次 10,333ms 第4次 10,392ms 第5次 10,170ms 第6次 11,317ms 第7次 10,641ms 第8次 10,381ms 第9次 10,234ms
第四次測試 使用 ADO.Net 配合Stored Procedure做查詢。結果如下
_SqlCmd.CommandText = "[uspTestUse]"; _SqlCmd.CommandType = CommandType.StoredProcedure; _SqlCmd.Parameters.Clear(); _SqlCmd.Parameters.AddWithValue("@param1", 1); var reader = _SqlCmd.ExecuteReader(); reader.Read(); int aa = Convert.ToInt32(reader[0]); reader.Close();
第0次 12,614ms 第1次 11,307ms 第2次 11,250ms 第3次 11,247ms 第4次 11,160ms 第5次 11,723ms 第6次 11,546ms 第7次 11,314ms 第8次 11,342ms 第9次 11,386ms
第五次測試 使用 Dapper 配合Stored Procedure做查詢。結果如下
var testData = _SqlConnect.QuerySingle<TestTable>("uspTestUse", new { param1 = 1 }, commandType: CommandType.StoredProcedure);
第0次 11,972ms 第1次 11,894ms 第2次 12,120ms 第3次 11,980ms 第4次 11,765ms 第5次 12,852ms 第6次 11,817ms 第7次 11,832ms 第8次 11,934ms 第9次 11,865ms
不做結語,因為只是簡單的測試
當初本來是在測試 Dapper的功能以及使用方法,後來想說是看看效能
目前只做查詢效率。以後有機會再做新增跟修改的效能資料
不過我猜結果應該是差不多
為什麼不做結語原因是
我寫這篇文章的環境是 Visual Studio 2017 + SQL Server 2014 Management Studio
但是我在另一台電腦上的環境是 Visual Studio 2015 + (localdb)\MSSQLLocalDB
結果剛好相反,速度最快的反而是 Dapper.Contrib.Extensions擴充的 Get最快的
平均速度 11秒。而ADO.Net和 Dapper 擴充的 Query速度平均是13秒左右
另外兩種 Stored Procedure的方式,都一樣平均11秒左右
其實 Dapper 底層也是轉換成 ADO.Net去做查詢,所以只要轉換的效能不要太差,理論上會差不多
第一次測測 使用Dapper的Get |
第二次測試 使用Dapper的Query |
第三次測試 使用ADO.Net的ExecuteReader |
第四次測試 使用ADO.Net配合StoredProcedure |
第五次測試 使用Dapper配合StoredProcedure |
第0次 13,863ms 第1次 12,793ms 第2次 13,443ms 第3次 13,356ms 第4次 14,810ms 第5次 14,985ms 第6次 12,894ms 第7次 12,764ms 第8次 12,822ms 第9次 13,703ms |
第0次 12,621ms 第1次 11,499ms 第2次 11,429ms 第3次 11,533ms 第4次 11,460ms 第5次 12,567ms 第6次 11,328ms 第7次 11,298ms 第8次 11,306ms 第9次 11,306ms |
第0次 11,512ms 第1次 10,161ms 第2次 10,417ms 第3次 10,333ms 第4次 10,392ms 第5次 10,170ms 第6次 11,317ms 第7次 10,641ms 第8次 10,381ms 第9次 10,234ms |
第0次 12,614ms 第1次 11,307ms 第2次 11,250ms 第3次 11,247ms 第4次 11,160ms 第5次 11,723ms 第6次 11,546ms 第7次 11,314ms 第8次 11,342ms 第9次 11,386ms |
第0次 11,972ms 第1次 11,894ms 第2次 12,120ms 第3次 11,980ms 第4次 11,765ms 第5次 12,852ms 第6次 11,817ms 第7次 11,832ms 第8次 11,934ms 第9次 11,865ms |