ViewState是.Net中提出的狀態保存的一種新途徑(實際上也是老瓶裝新酒);我們知道,傳統的Web程序保存狀態的方式有這樣幾種:
1、Application 這是Web應用程序生命期中的全局保存區,保存在Application中的數據是全局有效的;在Asp.Net中,有一個應用程序池,其中保存了數個(或數十個)應用程序實例,每一次請求都會從池中取一個實例來處理請求,在請求完畢之前,這個實例不會接受其他請求;這就出現一個問題,同一時間可能存在多個應用程序,也就是多個線程,這些線程都存在訪問Application的可能,所以在對Application中的對象進行處理的時候需要考慮線程同步的問題;實際上Application對象內部實現了一個線程鎖,調用它本身的Add、Remove等方法的時候會自動調用加鎖和解鎖的操作,但是出於性能考慮,對於直接通過索引器或其他方式得到其中的對象並進行操作的過程,Application並沒有自動處理線程同步,需要利用下列類似的代碼來處理:
Application.Lock();
((int)Application["Count"])++;
Application.Unlock();
值得注意的是,調用了Lock之後,如果沒有顯示的調用Unlock,那麼在這個請求結束的時候,Application對象會自動解鎖,這樣防止了造成死鎖的問題,但是為了代碼的健壯性,調用完Lock並且修改完畢應該立即的調用Unlock方法。
Application對象本質上就是一個Hash表,按照鍵值存放了對象,由於對象是全局並且存放在服務器,並且存在多線程同時訪問,所以,Application裡面存放的應該是訪問較多,修改較少並且是全局至少大部分功能會使用的數據,例如計數器或者數據庫連接串等。
2、Session 在Asp.Net內部,有一個StateApplication來管理Session,實際上就是一個輔助進程,處理Session到期、創建的特殊請求,在收到每一次請求的時候,輔助進程就會調用狀態服務器(可以通過Web.config設置不同的狀態服務器)來獲取Session,如果沒有對應該SessionId的Session,則會新建一個,然後綁定到上下文中(HttpContext);與Asp不同的是,Session的狀態服務器有多種,目前在Asp.Net內部實現了三種:
1) InProcStateClientManager 這是傳統的Session保存方式,但是還是有些細微差別
2) SqlStateClientManager 這是將Session保存到數據庫方式
3) OutOfProcStateClientManager 這是將Session保存到進程外的方式
Asp.Net的Session機制有一個特點,就是處理Session的輔助進程與保存Session的狀態服務器是分開的,按照MSDN的說法,有下列好處:
“因為用於會話狀態的內存不在 ASP.NET 輔助進程中,所以可以實現從應用程序故障的恢復。”
“因為所有狀態與輔助進程不存儲在一起,您可以干淨地跨多個進程對應用程序進行分區。這種分區可以顯著地提高多個進程的計算機上應用程序的可用性和可縮放性。”
“因為所有狀態與輔助進程不存儲在一起,所以您可以跨運行於多個計算機上的多個輔助進程對應用程序進行分區。”
Asp.Net的Session機制個人觀點,感覺靈活性比較好,內部實現也比較巧妙,但是實際上因為沒有做過多的測試,所以應用上會不會像它說的那麼美好,不敢打包票。有機會,我會單獨寫篇文章來深入的探討Asp.Net 內部的Session機制。
3、Cookie 這個沒甚麼好說,實際上Asp.Net與Asp的Cookie沒甚麼分別,也許這項技術毀譽參半,而且比較依賴客戶機實現,MS也沒什麼改進的。
4、ViewState 這是我們今天重點討論的;實際上ViewState並不神秘,就是一個Hidden字段,但是它是服務器控件狀態保存的基礎;不熟悉的朋友可以用IE查看Html源碼,找到一個名為"__VIEWSTATE"的Hidden字段,其中有一大堆亂七八糟的字符,這就是頁面的ViewState。
做過Web程序的人可能都有這種痛苦的體會,有時候為了處理頁面上面比較復雜的功能,常常會加很多Hidden,然後在服務器端用一大堆判斷來分析目前的狀態,寫起來煩人,寫完了代碼更是難看;實際上,ViewState就是幫我們系統的實現了保存控件狀態的功能,服務器端控件能夠在多次請求間保存狀態也全靠它。
好,介紹就到這裡,今天我們不是討論ViewState的使用,而是從內部來探探這個東西的本質。
我們首先建一個測試的頁面:
<%@ Page language="c#" Codebehind="ViewStateTest.aspx.cs" AutoEventWireup="false" Inherits="CsdnTest.ViewStateTest" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html>
<head>
<title>ViewStateTest</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio 7.0">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
</head>
<body>
<form id="ViewStateTest" method="post" runat="server">
<asp:Button ID="btnPostBack" Runat="server" Text="Post Back" Width="85px"></asp:Button>
<br/>
<asp:CheckBox ID="chkTest" Runat="server" Text="This is a check box"></asp:CheckBox>
</form>
</body>
</html>
這是用Vs.Net設計出來的一個簡單的頁面,裡面包含了一個服務器端的按鈕和一個CheckBox,然後我們在服務器端響應按鈕的事件:
private void btnPostBack_Click(object sender, System.EventArgs e)
{
[1] Response.Write( "ViewState :"+Request.Params["__VIEWSTATE"]+"<br/>" );
[2] string decodeValue = Encoding.UTF8.GetString( Convert.FromBase64String( Request.Params["__VIEWSTATE"] ) );