當然, viewstate 在 asp.net 中有個重要的角色。如果使用恰當,它能夠簡化頁面開發,改進用戶與站點的交互。如果置之不理,它能夠顯著增加站點響應大小,在連接速度慢的情況下,使您的響應時間更加緩慢。asp.net 2.0 的發布帶來了 viewstate 機制的一些改進,這使得 viewstate 使用更簡單,又不會防礙站點性能。這些改進包括:減少編碼數量,采用控件狀態從內容中分離出行為狀態,以及智能集成數據綁定控件。
viewstate 基本原理
在介紹 asp.net 2.0 viewstate 的改進之前,簡要總結目前版本中 viewstate 的用途和實現是適宜的。 viewstate 為 asp.net 開發人員解決了一個特定問題 — 保留服務器端不形成元素的控件的狀態。這很重要,因為 asp.net 中的大部分服務器端控件模型是根據這樣一個假設生成的,那就是 — 如果用戶回發到相同頁面,所有控件保持其狀態不變。也就是說,如果在處理請求期間修改任何控件的內容,任何后續 post 請求回到相同頁面時,您可以依賴于那些仍然存在的修改。作為一個活動的 viewstate 示例,嘗試運行下面顯示的代碼。
<%@ page language="c#" %>
<script runat="server">
protected override void onload(eventargs e)
{
int val = int.parse(_sum.innertext);
_sum.innertext = (val+1).tostring();
base.onload(e);
}
</script>
<html>
<body>
<form runat="server">
<h2>viewstate test page</h2>
<span id="_sum" runat="server">0</span>
<br />
<input type="submit" />
</form>
</body>
</html>
每次按下 submit 按鈕時,_sum 范圍值遞增。因為 viewstate 在請求期間保持以前的值,因此它將從上一次顯示的值開始,顯示 1、2、3、4 等等。如果想知道 viewstate 不可用時發生的事情,嘗試添加 enableviewstate='false' 作為范圍元素的一個屬性。因為以前的范圍值在請求處理時沒有傳播,所以不論頁面發布多少次,都將顯示值為 1。
在 asp.net 中, viewstate 完成基于控件的編程模型。如果沒有 viewstate ,一些控件(如文本框和下拉列表)在 post 請求期間保持狀態,而其他控件不保持,使用這些狀態各異的控件記錄一些特殊的情況是令人沮喪的體驗。使用 viewstate ,開發人員能夠專注于編程模型和用戶界面,而不用擔心狀態保持。還能對 viewstate 進行哈希或加密,以防止用戶篡改或解碼。
使用 viewstate 的另一個重要之處是在控件中發布服務器端更改事件。如果用戶改變了文本框中的值或切換了下拉列表中的選定元素,您就能夠注冊一個事件處理程序,引發事件時,執行代碼。這些控件比較其當前值與以前值,如果有任何過程預訂更改事件,以前值隱式存儲在 viewstate 中。如果禁用一個控件的 viewstate ,而您正在處理該控件的更改通知事件,因為總是假定以前值與窗體默認值相同,所以更改通知事件不會正確激發。
viewstate 問題
正如我早前指出的,在 asp.net 1.x 中, viewstate 有很多問題。默認情況下,它是啟用的,除非您知道在不需要使用時找到并禁用它,否則它能顯著增加頁面呈現的數據量。當使用數據綁定控件時,所有控件都使用 viewstate 保存回發后的狀態,這將變得異常痛苦。作為一個簡單而生動的示例,asp.net 頁面代碼:
<%@ page language="c#" %>
<%@ import namespace="system.data" %>
<%@ import namespace="system.configuration" %>
<%@ import namespace="system.data.sqlclient" %>
<script language="c#" runat="server">
protected override void onload(eventargs e)
{
string dsn = configurationsettings.appsettings["dsnpubs"];
string sql = "select * from authors";
using (sqlconnection conn = new sqlconnection(dsn))
using (sqlcommand cmd = new sqlcommand(sql, conn))
{
conn.open();
_dg1.datasource = cmd.executereader();
_dg1.databind();
}
base.onload(e);
}
</script>
<html>
<body>
<form id="form1" runat="server">
<asp:datagrid id="_dg1" runat="server" />
</form>
</body>
</html>
這個頁面有一個 datagrid 控件,該控件綁定對 pubs 數據庫中 authors 表格進行簡單查詢的結果。如果運行這個頁面(對連接字符串做出必要改正),查看 viewstate 字段,您可能吃驚地發現里面有超過 12,000 個字符。當查看頁面內容,意識到顯示瀏覽器中整個表格內容所需的實際字符數量大約是 1,600,您會更加吃驚。造成這個結果的原因之一是 viewstate 不僅編碼數據,而且編碼數據類型(元數據);同樣,base64 編碼一般要增加大約 33 % 的空間系統開銷。而且,大量的系統開銷只是為了保留 post 請求后的控件狀態,這似乎與時間不成比例,必須竭盡全力避免發生。
很明顯,如果您關心響應大小,那么決定何時禁用控件的 viewstate 是重要的。剛才的示例就是一個典型的情況,傳播了 viewstate ,但是從未使用,這是優化站點的 viewstate 使用時建議采用的主要規則:如果每次請求頁面時填充控件內容,禁用該控件的 viewstate 一般是安全(而明智)的。
另一方面,您可能決定利用 viewstate 保留控件狀態這一實事,并且只在頁面的首次 get 請求時,填充控件內容(只是貫穿后續 post 請求)。這省去了使用的任何后端數據源的往返行程。通過修改我的頁面來利用這一結論,我更改了 onload 方法,使它在填充 datagrid 前檢查 ispostback 標志,如以下代碼所示。
protected override void onload(eventargs e)
{
if (!ispostback)
{
string dsn = configurationsettings.appsettings["dsnpubs"];
string sql = "select * from authors";
using (sqlconnection conn = new sqlconnection(dsn))
using (sqlcommand cmd = new sqlcommand(sql, conn))
{
conn.open();
_dg1.datasource = cmd.executereader();
_dg1.databind();
}
}
base.onload(e);
}
當然,有一些其他可能更好的選擇,例如緩存服務器的查詢結果,每次發出請求時重新綁定控件。由您權衡狀態存儲的位置和特定體系結構與應用程序的重要性。
您可能注意到我曾小心提過,如果每次請求頁面時填充控件內容,那么禁用 viewstate “一般”是安全的。例外情況是,一些控件既使用 viewstate 保留行為,也保留一般狀態。如前所述,下拉列表和文本框控件使用 viewstate 存儲以前的值來正確發布服務器上的更改通知事件。同樣地,datagrid 類使用 viewstate 發布分頁、編輯和排序事件。遺憾的是,如果您想要使用 datagrid 中諸如排序、分頁或編輯的功能,則不能禁用其 viewstate 。對于嘗試生成快速有效站點的開發人員來說,服務器端控件 viewstate 的非全有即全無的狀況,是 asp.net 1.x 的服務器端控件模型另人沮喪的一面。
新聞熱點
疑難解答
圖片精選