轉自:http://www.cnblogs.com/sifang2004/archive/2006/05/31/414182.html
前言
我所說的“大全”,是吹的,需要大家的熱心的指教與補充。首先我的文章不會介紹什麼是ViewState以及怎麼使用它。如果對ViewState還不太明白的朋友,請參考MSDN上的這篇文章,雖然它是很久以前寫的,但對于了解ViewState還是非常不錯的。隻要對ViewState稍有了解,就會知道,Asp.net頁面中ViewState一般是存儲在頁面的一個隐藏域中:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="一堆亂七八糟的東西">
當我們浏覽頁面源檔案時,看到的那一大堆(特别是當頁面有個有大量資料的DataGrid,或在ASP.NET2.0中的GridView時)亂七八糟的東西的時候,那就是ViewState了。
基礎知識
因為,在ASP.NET2.0中ViewState的持久性存儲機制有了些新的變化,是以,還是簡單介紹下相關的東西。
在ASP.NET1.1中,隻提供了頁面隐藏域的持久性機制,這樣在某些情況下不得不放棄使用ViewState,試想下,如果你的DataGrid中有上萬條記錄(别認為這種變态的需要是沒有的,有人就碰到過),如果啟用了ViewState,你感保證你的IIS伺服器能承受得住嗎,網絡承受得主嗎?當然你是可以通過重寫Page.SavePageStateToPersistenceMedium()方法來更改你的存儲機制,但别忘了重寫Page.LoadPageStateFromPersistenceMedium(),它們可是一對的啊,至于如何寫,請檢視我的源碼。ASP.NET2.0 中的預設視圖狀态持久性機制依然是在頁上的一個隐藏 HTML 元素(一個 type 屬性設定為 "hidden" 的元素)中将狀态資訊保留為一個 Base64 編碼的字元串。ASP.NET 頁使用 HiddenFieldPageStatePersister 對象執行此項工作,并使用一個 IStateFormatter 執行個體對對象狀态資訊進行序列化和反序列化。或者,對于帶寬和資源有限的移動用戶端,您也可以使用 SessionPageStatePersister 類在伺服器上的 Session 對象中存儲頁的視圖狀态,其實也就多了個Session持久機制而已,讓我們把頁面狀态儲存在Session中,而不是頁面中,這對帶寬是一種節省。但你要深入的了解ViewState持久機制的話,抽象類PageStatePersister你是應該去了解的,要在不能支援現有視圖狀态持久性機制的用戶端上保留視圖狀态,可以擴充 PageStatePersister 類,引入您自己的視圖狀态持久性方法,并且可以使用頁擴充卡将 ASP.NET 應用程式配置為根據為其提供頁的用戶端的類型使用不同的視圖狀态持久性機制。從 PageStatePersister 類派生的類必須重寫 Save 抽象方法,以便在持久性媒體中存儲視圖狀态和控件狀态,同時重寫 Load 方法以提取狀态資訊。如果需要将視圖狀态和控件狀态序列化為字元串,可以使用通過 StateFormatter 屬性來通路的 IStateFormatter 對象。它可以高效地将對象狀态資訊序列化和反序列化為 Base64 編碼字元串。還可以重寫 StateFormatter 屬性以提供自己的對象狀态序列化機制,如何為之,我的代碼中都有介紹,很簡單,看看就明白了。
ViewState持久性機制
隐藏域
這個就不介紹了,預設的就是這種。就入前言中的那樣。
Session
在ASP.NET2.0中隻要重寫PageStatePersister屬性就可以了。
protected override PageStatePersister PageStatePersister
{
get
{
return new SessionPageStatePersister(Page);
}
}
要是在ASP.NET1.1中需要重寫LoadPageStateFromPersistenceMedium這兩個方法:
protected override object LoadPageStateFromPersistenceMedium()
{
return Session["ViewState"];
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
Session["ViewState"] = viewState;
RegisterHiddenField("__VIEWSTATE", "");
}
資料庫(我的示例是SQL Server2000)
在ASP1.1中,請注意下面紫色的那行,我也不太清楚那有什麼用,它讓我郁悶了好幾天,等下你就明白我的郁悶了。還有下面的代碼隻是湊我的源碼中拷貝出來的,你完全可以不這樣寫的,除了那些必要的外。
protected override void SavePageStateToPersistenceMedium(object state)
{
string viewStateID = "VIEWSTATE#" + Session.SessionID.ToString() + "#" + DateTime.Now.Ticks.ToString();
ClientScript.RegisterHiddenField("__VIEWSTATE_KEY", viewStateID);
ClientScript.RegisterHiddenField("__VIEWSTATE","");//請注意
try
{
if (losFormatter == null)
{
losFormatter = new LosFormatter();
}
StringWriter sw = new StringWriter();
losFormatter.Serialize(sw, state);
Common.ViewStateData vsd = new ViewStateData();
vsd.ViewStateID = viewStateID;
vsd.ViewState = sw.ToString();
da = new DataAccess();
string error = da.SaveViewState(vsd);
Response.Write(error);
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
}
protected override object LoadPageStateFromPersistenceMedium()
{
string viewState = string.Empty;
try
{
if (losFormatter == null)
{
losFormatter = new LosFormatter();
}
string stateID = Page.Request["__VIEWSTATE_KEY"].ToString();
da = new DataAccess();
viewState = da.LoadViewState(stateID);
}
catch
{
}
return losFormatter.Deserialize(viewState);
}
在ASP2.0中這行代碼基本是可以的,為什麼是基本呢,因為就是上面那行
ClientScript.RegisterHiddenField("__VIEWSTATE","");
有沒有這行,在Asp.net1.1中都是可行的,我也是參考過别人的代碼,這行就這麼加入了,加了這行後,隻是在頁面中多了個
<input type="hidden" name="__VIEWSTATE" value="" />
也就是運作後頁面的源檔案中有兩個這樣的東西。去掉那行也可以,是以我不明白語句是用做什麼的,請明白的告訴我吧。但是在Asp.net2.0中就不行,有如下錯誤:
The state information is invalid for this page and might be corrupted.
反正當時就是暈暈的,我以前從來沒有碰到過如是錯誤,去google也無所得,是啊,打死我也不知道是那句錯了啊,就這麼郁悶了兩天,問題無法解決,本人天生愚鈍的,我跟蹤視圖狀态的存入資料庫與從資料庫的讀取的整個過程,硬是找不到錯誤,我就反複思考這些代碼,惟有那行,我就是有點迷惑,為什麼還要頁面注冊一個“__VIEWSTATE”的隐藏域呢,于是我就注釋掉這行,居然可以運作了,是以我還是不明白那行是什麼用意,請知道的園友務必指點。
當然我們還可以通過寫一個PageStatePersister新子類也可以完成上述功能,這是ASP.NET2.0新增的:
namespace PageAdapter
{
using System;
using System.IO;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
public class DatabasePageStatePersister : PageStatePersister
{
public DatabasePageStatePersister(Page page): base(page)
{
}
//
// Load ViewState and ControlState.
//
public override void Load()
{
string viewState;
IStateFormatter formatter = this.StateFormatter;
DataAccess da = new DataAccess();
string stateID = base.Page.Request["__VIEWSTATE_KEY"].ToString();
viewState = da.LoadViewState(stateID);
Pair statePair = (Pair)formatter.Deserialize(viewState);
ViewState = statePair.First;
ControlState = statePair.Second;
}
//
// Persist any ViewState and ControlState.
//
public override void Save()
{
if (ViewState != null || ControlState != null)
{
if (Page.Session != null)
{
string viewStateID = "VIEWSTATE#" + base.Page.Session.SessionID.ToString() + "#" + DateTime.Now.Ticks.ToString();
base.Page.ClientScript.RegisterHiddenField("__VIEWSTATE_KEY", viewStateID);
Pair statePair = new Pair(ViewState, ControlState);
IStateFormatter formatter = this.StateFormatter;
// Serialize the statePair object to a string.
string serializedState = formatter.Serialize(statePair);
ViewStateData vsd = new ViewStateData();
vsd.ViewStateID = viewStateID;
vsd.ViewState = serializedState;
DataAccess da = new DataAccess();
string error = da.SaveViewState(vsd);
}
else
throw new InvalidOperationException("Session needed for StreamPageStatePersister.");
}
}
}
}
再有重寫PageStatePersister屬性就可以了:
protected override PageStatePersister PageStatePersister
{
get
{
return new DatabasePageStatePersister(Page);
}
檔案
這其實也跟資料庫的差不了多少,我這隻講ASP.NET2.0的,在ASP.NET1.1也應該差不多,但我沒有寫代碼調試:
還是用那種寫PageStatePersister新子類的辦法:
namespace StreamPageAdapter
{
using System;
using System.IO;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
//
// The StreamPageStatePersister is an example view state
// persistence mechanism that persists view and control
// state on the Web server.
//
[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
public class StreamPageStatePersister : PageStatePersister
{
public StreamPageStatePersister(Page page): base(page)
{
}
//
// Load ViewState and ControlState.
//
public override void Load()
{
Stream stateStream = GetSecureStream();
// Read the state string, using the StateFormatter.
StreamReader reader = new StreamReader(stateStream);
IStateFormatter formatter = this.StateFormatter;
string fileContents = reader.ReadToEnd();
// Deserilize returns the Pair object that is serialized in
// the Save method.
Pair statePair = (Pair)formatter.Deserialize(fileContents);
ViewState = statePair.First;
ControlState = statePair.Second;
reader.Close();
stateStream.Close();
}
//
// Persist any ViewState and ControlState.
//
public override void Save()
{
if (ViewState != null || ControlState != null)
{
if (Page.Session != null)
{
Stream stateStream = GetSecureStream();
StreamWriter writer = new StreamWriter(stateStream);
IStateFormatter formatter = this.StateFormatter;
Pair statePair = new Pair(ViewState, ControlState);
// Serialize the statePair object to a string.
string serializedState = formatter.Serialize(statePair);
writer.Write(serializedState);
writer.Close();
stateStream.Close();
}
else
throw new InvalidOperationException("Session needed for StreamPageStatePersister.");
}
}
// Return a secure Stream for your environment.
private Stream GetSecureStream()
{
string path = @"d:\a.txt";
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
return fs;
}
}
}
再重寫PageStatePersister屬性就可以了:
protected override PageStatePersister PageStatePersister
{
get
{
return new StreamPageStatePersister (Page);
}
通過上面的簡單介紹,我們應該有所了解了,隻是要明白的是:在ASP.NET1.1中我們隻能通過重寫age.SavePageStateToPersistenceMedium()和Page.LoadPageStateFromPersistenceMedium()來完成上述功能;而在ASP.NET2.0中,我們除了這外,還和通過寫PageStatePersister新子類和重寫PageStatePersister屬性來完成,我是沒有發現什麼不同,當然如果在下面的内容你就明白,寫PageStatePersister新子類的真正用處了。
使用頁擴充卡
由于狀态持久性機制與自适應呈現和用戶端功能有關,是以提供
MyPageAdapter
激活 ASP.NET 應用程式的 DatabasePageStatePersister。最後,提供了一個浏覽器功能 (.browser) 檔案來為特定類别的用戶端(在此例中為預設 Web 浏覽器)啟用
MyPageAdapter
器。
這些内容請具體看我提供的源碼中的PageAdapter工程。看了就明白了。
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
namespace PageAdapter
{
[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
public class MyPageAdapter : System.Web.UI.Adapters.PageAdapter
{
public override PageStatePersister GetStatePersister()
{
return new PageAdapter.DatabasePageStatePersister(Page);
}
}
}
最後,為了啟用
MyPageAdapter
擴充卡,您必須在 ASP.NET 應用程式的根目錄下建立一個名為 App_Browsers 的目錄,并在其中包括一個包含配置資訊的 .browser 檔案(其實這些都在你向工程中添加一個.browser檔案時vs2005會自動給你完成的。配置檔案中的 <refID元素訓示該配置重寫為 Default.browser 配置檔案中的預設浏覽器指定的值。此示例将
MyPageAdapter
用于 ASP.NET 網頁(但通常不使用擴充卡)。
<browsers>
<browser refID="Default" >
<controlAdapters>
<adapter
controlType="System.Web.UI.Page"
adapterType="PageAdapter.MyPageAdapter" />
</controlAdapters>
</browser>
</browsers>
這可以看源碼中的TestPageAdapter工程。該工程用來示範頁擴充卡的。
結束語
說得比較簡單,可能也不是很明白,至于各種持久機制的優劣,我也沒有專門測試過,而且最後一條“使用頁擴充卡”也不是屬于持久機制,隻是用了也擴充卡,我們就不要重寫
PageStatePersister屬性了,我看來好像用處不是很大,因為我們可以把重寫PageStatePersister的動作放在頁面基類中,其它所有頁面都繼承這個基類就可以了,在我代碼中就是這麼做的,用這個頁擴充卡還麻煩了,當然我也并太清楚頁擴充卡的這個東東。
另外,對我的源碼做個簡單說明:
1. PageAdapter工程
DatabasePageStatePersister.cs:PageStatePersister類的子類
MyPageAdapter.cs:頁擴充卡
DataAccess.cs和ViewSate.cs資料庫通路的,屬于輔助類。
2. StreamPageAdapter工程
這個與上面的相似,不多說了
3. SaveStateToDatabase工程
StateInHiddenField.aspx:測試預設的存儲機制,就是在看頁面源檔案時可以看到一大堆亂七八糟東西的。
StateInSession.aspx:存儲機制為Session
StateInDatabase.aspx:存儲機制資料庫,是重寫方法的那種,asp.net1.1,2.0都可以用的。
StateInDatabase2.aspx:寫PageStatePersister新子類的并重寫PageStatePersister屬性的那種
StateInFile.aspx:把ViewState儲存在伺服器中某個檔案夾中。
4. TestPageAdater工程。
用來測試也擴充卡用的。
還有,我這裡之發了ASP.NET2.0的源碼,如果想在ASP.NET1.1中測試,也都是基本可以通用的。