現存問題以及解決方案:在ASP.NET AJAX用戶端得到伺服器端的DataTable

來源:互聯網
上載者:User

摘要

ASP.NET AJAX由CTP升級到Beta之後,一個非常常見(我大概聽到了不止50個人的抱怨)的問題就是:在用戶端調用Web Method取得DataTable時候會發生“A circular reference was detected while serializing an object of type 'System.Reflection.Module'.”異常資訊。

本文將分析這個異常產生的原因並給出相應的解決方案,包括異常重現、異常原因、解決方案、範例程式碼下載等部分。

 

異常重現

讓我們先通過一個簡單的樣本程式重現這個異常,然後基於這個樣本程式修改並解決這個問題。

首先在頁面中聲明一個ScriptManager控制項。由於用戶端DataTable定義與Value-add包中,還需要引入PreviewScript.js指令碼:

<asp:ScriptManager ID="ScriptManager1" runat="server">    <Scripts>         <asp:ScriptReference Assembly="Microsoft.Web.Preview" Name="Microsoft.Web.Resources.ScriptLibrary.PreviewScript.js" />    </Scripts></asp:ScriptManager> 

接下來聲明一個HTML 按鈕和一個HTML <div>,分別用來引發對Web Method的調用以及顯示出返回的DataTable:

<input id="btnGetDataTable" type="button" value="Get DataTable" onclick="return btnGetDataTable_onclick()" /><div id="result"></div>

上面代碼中,點擊按鈕將調用一個名為btnGetDataTable_onclick()的用戶端JavaScript函數,該函數如下:

function btnGetDataTable_onclick() {    PageMethods.GetDataTable(cb_getDataTable);}

可以看到,PageMethods.GetDataTable()即為伺服器端名為GetDataTable()的Web Method的用戶端代理。伺服器端GetDataTable()方法的定義如下,注意該方法必須為靜態(static),且被[System.Web.Services.WebMethod]和[Microsoft.Web.Script.Services.ScriptMethod]兩個屬性所修飾:

[System.Web.Services.WebMethod][Microsoft.Web.Script.Services.ScriptMethod]public static DataTable GetDataTable(){    DataTable myDataTable = new DataTable();    myDataTable.Columns.Add(new DataColumn("Id", typeof(int)));    myDataTable.Columns.Add(new DataColumn("Name", typeof(string)));    for (int i = 0; i < 10; ++i)    {        DataRow newRow = myDataTable.NewRow();        newRow["Id"] = i;        newRow["Name"] = string.Format("Name {0}", i);        myDataTable.Rows.Add(newRow);    }    return myDataTable;}

上述代碼非常簡單,即建立了一個包含兩個列(Id和Name)的DataTable,並為該DataTable填充了10行資料。

讓我們返回到用戶端JavaScript部分,注意到在調用PageMethods.GetDataTable()時候我們為其指定了一個回呼函數,名為cb_getDataTable(),該JavaScript函數的定義如下:

function cb_getDataTable(result){    var contentBuilder = new Sys.StringBuilder();    for (var i = 0; i < result.get_length(); ++i)    {        contentBuilder.append("<strong>Id</strong>: ");        contentBuilder.append(result.getRow(i).getProperty("Id"));        contentBuilder.append(" <strong>Name</strong>: ");        contentBuilder.append(result.getRow(i).getProperty("Name"));        contentBuilder.append("<br />");    }        $get("result").innerHTML = contentBuilder.toString();}

上述回呼函數中只是簡單地對返回的DataTable(result參數)進行格式化後輸出到id為result的<div>中。注意其中使用了Sys.StringBuilder類,用來提高字串拼接效率,還使用了Beta中添加的$get()方法,用來根據id取得某個DOM元素。

這樣就完成了本程式,我們期望著用戶端將能夠正確解析伺服器端返回的DataTable,並將其顯示在頁面中。運行一下樣本程式並點擊頁面中的按鈕,“A circular reference was detected while serializing an object of type 'System.Reflection.Module'.”異常資訊“如我們所願”地出現了。

 

異常原因

異常原因也非常簡單:伺服器端DataTable中包含了若干個DataRow,而DataRow也包含著對DataTable的引用,自然將造成循環參考。這也正是我們在異常資訊中所見到的。

 

解決方案步驟一:修改Web.config

得知了原因之後,解決方案也變得明朗起來:自訂DataTable的JSON序列化組件。但ASP.NET AJAX的Value-add中已經為我們提供好了這個組件,即Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter,沒有必要重新發明輪子了。

開啟Web.config檔案,在<microsoft.web>\<scripting>\<webServices>中添加如下的代碼:

<jsonSerialization maxJsonLength="500000000">  <converters>    <add name="DataTableConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter"/>  </converters></jsonSerialization>

這樣即可使用ASP.NET AJAX的Value-add中內建的DataTableConverter完成對DataTable的序列化了。

如果一切正常的話,這樣也就夠了。滿懷欣喜地再次運行程式並點擊按鈕,卻得到了如下的錯誤資訊:

這是怎麼回事呢?將滑鼠移到result之上,展開之後卻看到一堆亂東西……這根本不是DataTable嘛!搞什麼呢?

 

 

解決方案步驟二:自訂JavaScript輔助函數

之所以會看到這一對亂東西,是因為Value-add中內建的DataTableConverter存在著一個Bug(這個Bug也太致命了……),會在序列化後的JSON字串結尾添加一個多餘的結束符。

瞭解了錯誤產生的原因之後,解決方案同樣非常簡單:編寫一個自訂的JavaScript輔助函數,將錯誤的DataTable解析為正確的即可:

function parseBetaDataTable(tbl){    eval('var tblrows = ' + tbl.dataArray.slice(0, tbl.dataArray.length-1) + ';');    tbl = new Sys.Preview.Data.DataTable(tbl.columns, tblrows);    return tbl;}

 然後在cb_getDataTable(result)中調用這個輔助函數,注意第一行的修改:

function cb_getDataTable(result){    result = parseBetaDataTable(result);        var contentBuilder = new Sys.StringBuilder();    for (var i = 0; i < result.get_length(); ++i)    {        contentBuilder.append("<strong>Id</strong>: ");        contentBuilder.append(result.getRow(i).getProperty("Id"));        contentBuilder.append(" <strong>Name</strong>: ");        contentBuilder.append(result.getRow(i).getProperty("Name"));        contentBuilder.append("<br />");    }        $get("result").innerHTML = contentBuilder.toString();}

曆經千辛萬苦之後,終於大功告成!

 

完成後的樣本程式

再次運行頁面並點擊其中的按鈕,將如我們所願地得到正確的運行結果。是不是很有成就感呢?

 

範例程式碼下載

本文的樣本程式在此下載:ASPNETAJAXDataTable.zip

 

參考文獻

  1. http://forums.asp.net/thread/1442553.aspx
  2. http://forums.asp.net/thread/1251349.aspx

 

寫作隨想

  1. 寫文章要認真,做任何事情也是如此。認真兩個字說起來簡單,做起來卻不容易。
  2. 寫文章要以使用者的角度,用商業的思維考慮。如何讓讀者有興趣,喜歡,讀完,留下評論……這是個學問。
  3. 研究、解決問題花費一小時,寫作花費一小時。
  4. Windows Live Writer的Bug太多,寫作過程發生3次異常關閉,好在均有備份,沒有太大損失。

(PS:本文選自我的Atlas著作第II卷,其中第I卷將在明年一月出版,希望大家支援。)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.