當在 WebService 中直接返回 DataTable 時,會出現循環參考的異常,
何為循環參考?循環參考就是在引用上,
A 引用了 B,而 B 又引用了 A,這就是一個典型的循環參考,
當有循環參考時,在進行 JSON 序列化時會報出出現循環參考的異常,
例如
我在 WebService 中返回的是一個 DataTable 類型,
DataTable 算是一個極其複雜的物件類型了,
這個類型在進行 JSON 序列化時,便會產生一個循環參考,
在序列化其中的 System.Reflection.Module 對象時會發生循環參考,
從而會導致 JSON 序列化失敗,導致整個的非同步呼叫 WebService 失敗,
看一下這個失敗的例子吧,
首先看 .asmx
[WebMethod]
[System.Web.Script.Services.ScriptMethod]
public DataTable GetDataTable()
{
DataTable myTable = new DataTable();
DataRow myRow;
myTable.Columns.Add("ID", typeof(string));
myTable.Columns.Add("Name", typeof(string));
myTable.Columns.Add("Sex", typeof(string));
myTable.Columns.Add("FatherName", typeof(string));
myTable.Columns.Add("Address", typeof(string));
string conStr = WebConfigurationManager.
ConnectionStrings["Demo"].ConnectionString;
string sqlStr = "SELECT 社會安全號碼碼,學生姓名,性別,家長姓名,家庭地址 FROM 學生";
using (SqlConnection sqlCon = new SqlConnection(conStr))
{
sqlCon.Open();
using (SqlCommand sqlCom = sqlCon.CreateCommand())
{
sqlCom.CommandText = sqlStr;
using (SqlDataReader sqlDr = sqlCom.ExecuteReader())
{
while (sqlDr.Read())
{
myRow = myTable.NewRow();
myRow["ID"] = sqlDr.GetSqlString(0).Value;
myRow["Name"] = sqlDr.GetSqlString(1).Value;
myRow["Sex"] = sqlDr.GetBoolean(2).ToString();
myRow["FatherName"] = sqlDr.GetSqlString(3).Value;
myRow["Address"] = sqlDr.GetSqlString(4).Value;
myTable.Rows.Add(myRow);
}
}
}
}
return myTable;
}
顯而易見,上面這個 WebService 返回的是一個 DataTable 對象
再看一下用戶端 JavaScript 代碼
function PageLoad() {
深入淺出_ASP.NET_AJAX.Demo__10__Use.
set_timeout(2000);
深入淺出_ASP.NET_AJAX.Demo__10__Use.
set_defaultFailedCallback(OnFailedCallback);
深入淺出_ASP.NET_AJAX.Demo__10__Use.
set_defaultSucceededCallback(OnSucceededCallback);
}
function GetDataTable_onclick() {
深入淺出_ASP.NET_AJAX.Demo__10__Use.GetDataTable();
}
function OnFailedCallback(error, userContext, methodName) {
var msg = "";
if (methodName == "GetDataTable") {
msg += "\nGetDataTable 在非同步執行期間發生下列錯誤";
}
msg += "\n 異常資訊: " + error.get_message();
msg += "\n 異常類型: " + error.get_exceptionType();
msg += "\n 狀態代碼 : " + error.get_statusCode();
msg += "\n 堆棧資訊: " + error.get_stackTrace();
alert(msg);
}
function OnSucceededCallback(result, userContext, methodName) {
}
執行中由於 DataTable 在序列化時會發生循環參考,所以很明顯,會失敗
單擊這個 Button 後的結果為
以上是在從 WebService 中直接返回 DataTable 時出現的異常,
那麼該如何解決這個問題呢,使得能夠從服務端順利的返回 DataTable 中的資料呢?
事實上,應該明確以下幾點,
在服務端與用戶端進行傳遞資料的格式是 JSON (JavaScript Object Notation),
這個格式簡單輕便,
最常用的以一般的字串類型, List<T> 集合類型以及
字典Dictionary<string ,T>類型進行資料的傳遞和轉換,
其中較為特殊的是 Dictionary<string ,T>,
其中的第一個類型必須是 string ,否則在用戶端將無法識別,
而 DataTable 或者 DataSet 這些複雜資料類型已經遠遠超出了上面的三種類型的範圍,
所以無法進行正常的 JSON 序列化,
那麼如何才能正常的把 DataTable 中的資料正確傳遞給 JavaScript 呢?
其中有兩種方法對這種情況可以解決,
第一種是使用 JavaScriptConverter 來實現,
第二種則是自訂資料類型,
把 DataTable 這些複雜資料類型先轉換為上面的三種資料類型再傳遞。
本次主要講解一下的是第二種方法,
第一種方法相對來說更為複雜,您必須定義自己的 Converter 來實現,
同時將這個 Converter 在 Web.Config 中實現註冊,
並且必須自行控制解除或者設定循環參考,相對來說比較繁瑣,
但是第二種方式則比較簡單,
因為我是採用在服務端將 DataTable 先轉化為自訂資料類型,
然後再返回我的自訂資料類型,從而間接的實現了返回 DataTable ,
而第一種使用 JavaScriptConverter 則是直接返回的 DataTable 。
改進如下(使得上個失敗的例子可以成功返回 DataTable 中的資料)
通過添加一個類來實現將 DataTable 轉換為 List<T> 或者 Dictionary<string ,T>來返回,
類的代碼如下
public class Demo__10__Use__Converter
{
private DataTable myTable;
//在建構函式中傳入一個 DataTable
public Demo__10__Use__Converter(DataTable myTable)
{
this.myTable = myTable;
}
//注意返回的類型,將 DataTable 轉換後返回的類型是 List<T>
//此處定義了多級的 List
//第一級 List 來儲存 DataTable 中的每一行,也就是儲存第二級 List
//而第二級 List 則是用來儲存 DataTable 中每一行中的資料,即 Dictionary
//而 Dictionary 則是用來儲存每一個儲存格的數值
public List<List<Dictionary<string, string>>> Converter()
{
//第一級 List
List<List<Dictionary<string, string>>> firstList =
new List<List<Dictionary<string, string>>>();
//遍曆 DataTable 的每一行
for (int i = 0; i < this.myTable.Rows.Count; i++)
{
//第二級 List
List<Dictionary<string, string>> secondList =
new List<Dictionary<string, string>>();
//遍曆 DataTable 的每一列
for (int j = 0; j < this.myTable.Columns.Count; j++)
{
//擷取這個儲存格所在列的列名
string columnName = this.myTable.Columns[j].ColumnName;
Dictionary<string, string> myDictionary =
new Dictionary<string, string>();
//將這個儲存格的數值儲存在字典中
myDictionary[columnName] = this.myTable.Rows[i][j].ToString();
secondList.Add(myDictionary);
}
firstList.Add(secondList);
}
return firstList;
}
}
然後再在 WebService 中更改一點點就 OK 了
接下來就是要在 JavaScript 中解析,
傳遞過來的 List<List<Dictionary<string,string>>>類型並且輸出了
再看一下結果吧
該部分的代碼需要仔細斟酌,慢慢理解
以上就是在服務端傳遞 DataTable 到用戶端 JavaScript 的整個範例,
大家可以慢慢研究。
2010—1—27