Dapper 簡單測試

  • 6416
  • 0
  • ORM
  • 2018-01-07

簡單說明Dapper 以及官方擴充Dapper.Contrib.Extensions 使用上的小測試

和ADO.Net 做一些效能測試比較

 


很多前輩介紹過Dapper

列出一些我參考過的文章

黑案執行緒-前輩的 短小精悍的.NET ORM神器 -- Dapper

mrkt 的程式學習筆記-前輩的 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 { getset; }         // int
    public Guid Guid { getset; }      // uniqueidentifier
    public string Name { getset; }    // 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