寫過 Dapper 之後,已不太能忍受傳統 ADO.NET 的囉嗦寫法:

using (var cn = GetConnection()) 
{
    cn.Open();
    var cmd = cn.CreateCommand();
    cmd.CommandText = "SELECT ... FROM XXX WHERE ... @p1 ... @p8";
    cmd.Parameters.Add("p1", SqlDbType.VarChar).Value = p1;
    cmd.Parameters.Add("p2", SqlDbType.NVarChar).Value = p2;
    cmd.Parameters.Add("p3", SqlDbType.DateTime).Value = p3;
    //...繼續設參數
    cmd.Parameters.Add("p8", SqlDbType.VarChar).Value = p8;
    var dr = cmd.ExecuteReader();
    while (dr.Read()) {
        //...
    }
}

十幾行程式換成 Dapper 三行就搞定,滿臉皺紋的大嬸變身皮膚吹彈可破的少女,有過這種體驗誰還回得去?

using (var cn = GetConnection()) 
{
    var res = cn.Query("SELECT ... FROM XXX WHERE ... @p1 ... @p8", new {
        p1, p2, p3, p4, p5, p6, p7, p8
    });
    foreach (var row in res) {
        //...
    }
}

所以,每當遇到有舊程式需要修改,我會儘量將 SqlCommand 寫法改成 Dapper 再調整,但有一種狀況比較麻煩:

public DataTable QuerySomething(string arg1, int arg2) 
{
    //... ADO.NET IDbCommand 寫法
}

回傳型別為 DataTable 的共用函式,已被應用在很多地方,要改傳 IEnumerable<T> 工程浩大;更何況,某些古老技術如 WebForm DataGrid、RDLC 非 DataTable 不吃。這種情況,即使要改用 Dapper,也必須將查詢結果轉回 DataTable。

古早以前我試過一種做法是用 Reflection 將 IEnumerable<T> 轉成 DataTable,但費工了點。後來,我找到偷吃步的做法 - 用 Json.NET 將 IEnumerable<T> 序列化成 JSON,再反序列化回 DataTable:

using (var cn = GetConnection())
{
    var res = cn.Query("SELECT * FROM Records");
    var json = JsonConvert.SerializeObject(res);
    var dt = JsonConvert.DeserializeObject<DataTable>(json);
    //...
}

很神奇吧! (Json.NET 好強大,叫人怎能不愛她?)

不過,今天我學到了正統解法。除了 Query() 跟 Execute(),Dapper 其實也實作了 ExecuteReader() 擴充方法,所以用 Dapper 產生 DataTable 最有效率的做法是:

using (var cn = GetConnection()) 
{
    var dr = cn.ExecuteReader("SELECT ... FROM XXX WHERE ... @p1 ... @p8", new {
        p1, p2, p3, p4, p5, p6, p7, p8
    });
    var dt = new DataTable();
    dt.Load(dr);
    return dt;
}

就這麼簡單!

學會這招,未來為老程式做拉皮手術就更方便囉~

Tips of how to convert the query results from Dapper into ADO.NET DataTable.


Comments

# by kenchien

提醒,如果撈取的資料有p key重複會產生錯誤,可以外面加一層DataSet並設定屬性DataSet.EnforceConstraints=false,直接合併成一行也可以 DataTable dt = new DataSet(){EnforceConstraints=false}.Tables.Add();

# by Jeffrey

to kenchien, 感謝分享。

# by Whatever

var dt = new DataTable(); dt.Load(dr); return dr; ??? return dr 還是 return do?

# by Jeffrey

to Whatever, 恭喜發現彩蛋(打錯字硬拗),return dt 才對,謝謝指正。

Post a comment