现存问题以及解决方案:在ASP.NET AJAX客户端得到服务器端的DataTable

今天我做项目的时候遇到一个问题 pageMethed 未定义 经过查询资料 是因为在用ajax控件

 <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
</asp:ScriptManager>

需要ajax 配置的 在我加上以后    在ajax 配置上很容易忽视细节 所以在这里

<href="http://www.cnblogs.com/wanglinglong/archive/2009/12/15/1624792.html">ajax配置文件</a>


我把ajax配置记录上

 

   又报了如下的错误 经过我的查找 我找到了以下的答案:

 
        <!-- Uncomment this line to customize maxJsonLength and add a custom converter -->
        <jsonSerialization maxJsonLength="50000000">
          <converters>
            <add name="DataSetConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataSetConverter, Microsoft.Web.Preview, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
            <add name="DataRowConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataRowConverter, Microsoft.Web.Preview, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
            <add name="DataTableConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter, Microsoft.Web.Preview, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
          </converters>
        </jsonSerialization> 
在web config配置文件里加上这段话 这个目录下<system.web.extensions><scripting><webServices> 才没有报已下的错误了 所以记录下 以后不会再出这样的错误

 

 

 以下错误和方案:

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();
}

历经千辛万苦之后,终于大功告成!

 

完成后的示例程序

再次运行页面并点击其中的按钮,将如我们所愿地得到正确的运行结果。是不是很有成就感呢?

posted @ 2009-12-15 14:37  不必太用力  阅读(295)  评论(0编辑  收藏  举报