如何返回一个 DataTable 到客户端 JavaScript
当在 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 的整个范例,
大家可以慢慢研究。