2014-04-02

TableAdapter傳回單一值Query型態問題

因為之前開發.net程式都是用vb在寫,對於資料型態的要求並不那麼嚴謹,會自動判斷並轉型,但後來改用C#來寫,每個資料型態都很要求,萬一型態不同,必須指定轉型的資料型態。

最近有些DataSet檔案裡面,需要修改TableAdapter裡面某些回傳單一值的Query,但修改完一跑,怎麼程式報錯?看看程式裡面,竟然回傳的型態變成Object了!!


如下圖,有個TableAdapter,除了預設的Fill方法外,還有兩個傳回單一值的query
以GetUserCount為範例,該sql應該會回傳一個int型態的資料



這個是Query剛建立好,在程式中的狀況,可看見提示是回傳int?,XSD原始碼內的ScalarCallRetval也顯示"System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",表示建立query之後可抓到正確的型態(為了方便截圖所以手動斷行)




接著對Query做修改,這裡其實什麼都沒改,就一直下一步直到按下完成






然後去看DataSet的xsd原始檔,竟然變成"System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",型態是Object了...@@



當然程式內如果相信第一次建立Query後的回傳型態,沒轉型,就會有錯…
TableAdapter裡面第二個GetUserName也是一樣,只是第一個是int第二個是string,只要修改過全都會變成Object


如果要解決,只能從修改程式下手,強迫在每個Query都指定資料型態才行!如下:
int? count = (int?)da.GetUserCount();
string name = (string)da.GetUserName(1);


好奇的是,既然第一次產生Query,按下完成後可以取得sql回傳的型態,為什麼再進去編輯,不會再從sql裡面再取得一次回傳的型態,就硬要把型態改為Object勒?不然也可以維持第一次的型態值不動啊!!@@

而在網路上搜尋的結果,這東西從Visual Studio 2005開始就一直存在相同的問題了,看來微軟只建議從程式面指定轉型下手,卻不認為第一次產生可抓到型態,第二次之後就強迫變成Object,這樣的方式是bug.....

--

更新

經朋友幫我轉寄給微軟工程師測試後,得到了回答,以下是往來內容:

--

工程師:


這個問題不是 Bug,原因說明如後。

傳回單一值 (Single Value)  TableAdapter 查詢會根據查詢傳回資料型別(而不是傳回物件的 ExecuteScalar 方法),比方說,TableAdapter 查詢選取資料型別為整數 (integer) 的單一欄位(Single Column),則查詢的傳回值為整數。

單一純量 (Single Scalar) 查詢無法確知使用者會傳回什麼型別的資料,故需要對傳回值進行型別轉換,如下圖是 MSDN 文件「如何:執行 TableAdapter 查詢」(網址:http://msdn.microsoft.com/zh-tw/library/ms233822.aspx),「執行可傳回單一 (純量值的 TableAdapter 查詢」一節的說明:

--

我:

可是第一次建立的時候可以取得型態呢!只有在修改過後才會變成object,如果第一次可以正確取得型態,為什麼第二次之後不行呢?

--

工程師:


如客戶先前 PDF 檔所述,使用 TableAdapter 組態精靈設定完畢之後,此時 .xsd 檔的 ScalarCallRetval型別 System.Int32 是正確的,但接著,再執行精靈進行設定(即便沒有改任何東西),這時候,ScalarCallRetval 型別就改變為 System.Object

原因是因為,於第一次啟用精靈進行設定時,精靈會連線到資料庫,因此可以知道資料表之欄位型別,判斷出回傳的確實型別為何,但第二次使用精靈,已經沒有再參照到原始的資料表欄位,故會變成是存量  (Scalar) ,亦即被視為是 Object 型別的資料。

這部分的說明請參考SqlCommand.ExecuteScalar 方法」(網址:來自 http://msdn.microsoft.com/zh-tw/library/system.data.sqlclient.sqlcommand.executescalar(v=vs.110).aspx),如下圖所示傳回值的型別是 System.Object

----------------------------------------

所以這個問題還是要靠自己勤勞點在程式碼轉型囉!!