對同事做了一場 Dapper 威力展示。

原本打算示範類似 cn.Query("SELECT ColName FROM TblName").First().ColName.Split(',').First() 讀取 CSV 欄位值 Split() 分割成字串陣列再用 First() 得到第一節的 Dapper + LINQ 組合美技。孰料槍枝膛炸傷了手指,糗!

用以下程式重現錯誤:(實際例子比這個複雜些,需取得多個欄位進行處理,有借用 Dapper 不宣告結果型別卻可直接 .ColName 屬性取值的方便性)

using Dapper;
using System;
using System.Data.SqlClient;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var cn = new SqlConnection(Constants.CnStr))
            {
                var res = cn.Query("SELECT 'A,B,C' AS CSV, 123 AS C2").Single();
                var a = res.CSV.Split(',').First();
                Console.WriteLine(a);
                Console.Read();
            }
        }
    }
}

Split(',').First() 時拋出 'System.Array' does not contain a definition for 'First' 的怪異訊息:

爬文這通常是忘了 using System.Linq 造成,但程式確實有引用,且編譯也沒出錯,不是嗎?

迷惘了一陣子,我才驀然想起:啊,幹! 是 dynamic!!

類似問題在應用 ViewBag 時其實早已遇過多次:

當未指定泛型型別,Dapper.Query() 會傳回 dynamic 型別,與 ViewBag 特性相似。簡單來說,由於 res 是 dynamic,.CSV、.Split() 要靠執行期間透過 System.Runtime.CompilerServices、System.CSharp.RuntimeBinder 命名空間的物件與方法隔水加熱完成,而由於無法事先知道型別,dynamic 又常會被當成 object 處理,或許可以解釋 Split() 結果為何不是 string[] 而是 System.Array。(以上純屬猜測,.NET 底層運作超出我的守備範圍,屬於沒有槍頭也能捅進去的高人)

知道原因要解決就簡單了。用 (string) 將 res.CSV 轉成 string,搞定!

Hint of using Dapper dynamic type returned by Query() with LINQ methods.


Comments

# by 凱大

dynamic 自 現在大量使用型別推斷作為輔助的情況下 真的是如同自廢武功 dynamic 因為是design time沒有型別 而不是 可能為任意型別的情況下 像是 擴充方法 , 泛型推斷, LINQ 等等 都是惡夢 orz...

# by 卡比

我一直的感覺是因為資料未真正從 db 傳回,只是有個 reference 指在遠處的資源,這個想法好像未足夠反映真實

# by Clark

我都是用帶回傳型別的多載XD

Post a comment