本文來自《ASP.NET AJAX程式設計 第II卷:用戶端Microsoft AJAX Library相關》的第三章《非同步呼叫Web Service和頁面中的類方法》,請同時參考本章的其他文章。
3.7.6 DataTable和DataSet
DataTable和DataSet是ADO.NET中的重要概念,這兩個對象均非常複雜,其中包含了大量的複雜類型和循環參考。為了能夠在ASP.NET AJAX非同步通訊層中傳遞這兩種類型的資料,微軟公司在ASP.NET AJAX Futures CTP版本中給出了自訂的DataTable和DataSet轉換方案——即藉助於ASP.NET AJAX強大的可擴充性,通過自訂JavaScriptConverter實現DataTable和DataSet類型的用戶端/伺服器端自動轉換。
參考:在某些情況下,我們也需要通過編寫自訂的JavaScriptConverter來為實際項目中遇到的某些複雜類型給出自訂的轉換方案。關於JavaScriptConverter組件的基本知識,請參考第2章;關於自訂JavaScriptConverter的編寫方法,將在第III卷中介紹。
若想在ASP.NET AJAX非同步通訊層中傳遞DataTable和DataSet這兩種類型的資料,首先我們應該確保安裝了ASP.NET AJAX的Futures CTP部分(關於對ASP.NET AJAX的Futures CTP部分的介紹以及安裝方法,請參考第I卷),並在Web網站中添加了對Microsoft.Web.Preview.dll程式集的引用(將該程式集拷貝到Web網站的\bin檔案夾下),3-31所示。
圖3-31在Web網站中添加對Microsoft.Web.Preview.dll程式集的引用
隨後讓我們在web.config檔案中啟用ASP.NET AJAX Futures CTP中內建的DataTable和DataSet相關JavaScriptConverter組件。將如下代碼添加至<configuration />\<system.web.extensions />\<scripting />\<webServices />節中:
<jsonSerialization>
<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>
進行上述配置之後,我們即可ASP.NET AJAX非同步通訊層中傳遞DataTable和DataSet了,讓我們通過一個執行個體程式說明具體的使用方法。
在本樣本程式,我們將通過ASP.NET AJAX非同步通訊層從伺服器端Web Service方法中取得一個DataTable,並將其中的資料顯示為頁面中的一個HTML <table />。首先從Web Service開始,代碼如下,注意不要忘記為該Web Service類添加[System.Web.Script.Services.ScriptService]屬性:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class DataService : System.Web.Services.WebService
{
[WebMethod]
public DataTable GetDataTable(string tableName)
{
// 設定DataTable的名稱
DataTable table = new DataTable(tableName);
// 為該DataTable添加兩列
table.Columns.Add(new DataColumn("Id", typeof(int)));
table.Columns.Add(new DataColumn("Name", typeof(string)));
// 添加5行
for (int i = 0; i < 5; ++i)
{
DataRow newRow = table.NewRow();
newRow["Id"] = i;
newRow["Name"] = string.Format("name {0}", i);
table.Rows.Add(newRow);
}
return table;
}
}
然後再用戶端ASP.NET頁面中添加ScriptManager控制項,並引入該Web Service:
<asp:ScriptManager ID="sm" runat="server">
<Services>
<asp:ServiceReference Path="Services/DataService.asmx" />
</Services>
</asp:ScriptManager>
來到頁面的UI部分:添加一個用來出發對伺服器端Web Service方法的非同步呼叫的按鈕和一個用來顯示由伺服器端傳回值構造出的HTML <table />的<div />:
<input id="btnGetDataTable" type="button" value="Get DataTable"
onclick="return btnGetDataTable_onclick()" />
<div id="result">
</div>
btnGetDataTable按鈕的click事件的處理函數定義如下:
function btnGetDataTable_onclick() {
DataService.GetDataTable("My Table", onSucceeded);
}
此時,如果一切順利的話,在回呼函數onSucceeded()中我們已經可以看到伺服器端返回的DataTable的基本結構了。比如下面的代碼就使用Sys.Debug.traceDump()方法(關於Sys.Debug.traceDump()方法,請參考第1章中的介紹)查看了result對象的詳細結構:
function onSucceeded(result) {
Sys.Debug.traceDump(result);
}
在Visual Studio的“Output”視窗中,我們即可看到該用戶端DataTable的完整結構以及其中所包含的資料,3-32所示。
圖3-32 用戶端DataTable的完整結構以及其中所包含的資料
當然,在Visual Studio的調試器中,我們也可以直接查看該DataTable的結構和資料,3-33所示。
圖3-33 伺服器端DataTable類型在用戶端的結構
從圖3-32和圖3-33可以看到,ASP.NET AJAX非同步通訊層為伺服器端DataTable產生的用戶端JavaScript相應類型還是比較簡單的,很多原始DataTable中的資料之間的關係和約束均沒有保留下來。不過在一般的開發情境中,這些資訊已經足夠滿足我們的使用需求了——畢竟,DataTable中的所有“資料”都絲毫不差地保留了下來。
參考:ASP.NET AJAX在Futures CTP版本中提供了更為完備的用戶端DataTable類型,其完全限定名為Sys.Preview.Data.DataTable。有關該類型的詳細介紹,請參考本書8.2.2節。
接下來讓我們回到該樣本程式的編寫中來,完成回呼函數onSucceeded(),將該用戶端DataTable中的資料以HTML <table />的形式顯示出來。完成後的回呼函數onSucceeded()代碼如下,其中代碼流程均有詳細注釋,這裡不贅:
function onSucceeded(result) {
// 測試
//Sys.Debug.traceDump(result);
//debugger;
// 得到兩列的名稱
var idColName = result.columns[0].name;
var nameColName = result.columns[1].name;
// 得到DataTable中的行集合
var rows = result.rows;
// 建立表格頭
var builder = new Sys.StringBuilder("<table border=1>");
builder.append(
String.format(
"<tr><td>{0}</td><td>{1}</td></tr>",
idColName,
nameColName
)
);
// 建立表格內容
for (var rowIndex = 0; rowIndex < rows.length; ++ rowIndex) {
builder.append(
String.format(
"<tr><td>{0}</td><td>{1}</td></tr>",
rows[rowIndex][idColName],
rows[rowIndex][nameColName]
)
);
}
builder.append("</table>");
// 顯示表格
$get("result").innerHTML = builder.toString();
}
至此,我們已經完成了本樣本程式的編寫。運行該程式並點擊“Get DataTable”按鈕,將看到3-34所示的介面。
圖3-34 從伺服器端取得DataTable對象並顯示在頁面中
若是想得到DataSet對象,那麼ASP.NET AJAX非同步通訊層也有著不錯的支援。在上面的DataService Web Service類中再添加如下一個方法:
[WebMethod]
public DataSet GetDataSet(string[] tableNames)
{
DataSet dataSet = new DataSet();
// 根據傳入的DataTable名稱建立各個DataTable
for (int i = 0; i < tableNames.Length; ++i)
{
dataSet.Tables.Add(GetDataTable(tableNames[i]));
}
return dataSet;
}
該方法接受一個字串數組,並根據該數組的長度建立相應個數的DataTable,然後將這些DataTable打包到一個DataSet中返回給用戶端。
在用戶端,我們可以使用如下JavaScript調用該Web Service方法:
var dataTableNames = ["My Table 1", "My Table 2", "My Table 3"];
DataService.GetDataSet(dataTableNames, onSucceeded);
在onSucceeded()回呼函數中,返回的用戶端DataSet在Visual Studio調試器中顯示出的結構3-35所示,可以看到該DataSet包含了3個DataTable對象。
圖3-35伺服器端DataSet類型在用戶端的結構
雖然這個用戶端版本的DataSet仍舊比較簡單,但一般來講,這樣的資料結構已經足夠我們使用了。