字串 Split() 結果出現 System.Array 不支援 LINQ 方法錯誤
| | 3 | |
對同事做了一場 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 時其實早已遇過多次:
- 【茶包射手日記】CSHTML ViewBag無法使用擴充方法
- ViewBag dynamic 特性導致無法使用 LINQ 語法)
- 擴充方法參數傳入 dynamic 型別出錯
- 方法多載(Method Overloading)與 dynamic
當未指定泛型型別,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