ASP.NET在頁面生命週期中有兩個很重要的階段分別是載入視圖和儲存視圖,下面就設兩個階段作具體的討論,最後討論一下ASP.NET 2.0 引入的一個新的視圖機制ConrolState
在讀本文之前各位必須要清楚asp.net頁面本身Page類就是個控制項,所以下文中所指的控制項既指Page本身也指包含在Page中的頁面上的各個控制項,另外本文討論asp.net的兩套視圖機制一個就是大家熟悉的ViewState以下用視圖二字表示,還有一套機制是asp.net 2.0新增的ConrolState以下用控制項狀態四個字在文中表示,請注意區分
1、視圖的儲存
圖1
1 所示,視圖在儲存過程中會調用一個SaveAllState函數:
代碼
private void SaveAllState()
{
if (this._needToPersistViewState)
{
Pair state = new Pair();
IDictionary dictionary = null;
if ((this._registeredControlsRequiringControlState != null) && (this._registeredControlsRequiringControlState.Count > 0))
{
dictionary = new HybridDictionary(this._registeredControlsRequiringControlState.Count + 1);
foreach (Control control in (IEnumerable) this._registeredControlsRequiringControlState)
{
object obj2 = control.SaveControlStateInternal();
if ((dictionary[control.UniqueID] == null) && (obj2 != null))
{
dictionary.Add(control.UniqueID, obj2);
}
}
}
if ((this._registeredControlsThatRequirePostBack != null) && (this._registeredControlsThatRequirePostBack.Count > 0))
{
if (dictionary == null)
{
dictionary = new HybridDictionary();
}
dictionary.Add("__ControlsRequirePostBackKey__", this._registeredControlsThatRequirePostBack);
}
if ((dictionary != null) && (dictionary.Count > 0))
{
state.First = dictionary;
}
Pair pair2 = new Pair(this.GetTypeHashCode().ToString(NumberFormatInfo.InvariantInfo), base.SaveViewStateRecursive());
if (this.Context.TraceIsEnabled)
{
int viewstateSize = 0;
if (pair2.Second is Pair)
{
viewstateSize = base.EstimateStateSize(((Pair) pair2.Second).First);
}
else if (pair2.Second is Triplet)
{
viewstateSize = base.EstimateStateSize(((Triplet) pair2.Second).First);
}
this.Trace.AddControlStateSize(this.UniqueID, viewstateSize, (dictionary == null) ? 0 : base.EstimateStateSize(dictionary[this.UniqueID]));
}
state.Second = pair2;
this.SavePageStateToPersistenceMedium(state);
}
}
從這段代碼我們可以看出在LoadAllState中,ASP.NET最先使用了一個System.Web.Pair對象他有兩個欄位First、Second都是object類型
IDictionary dictionary = null;
if ((this._registeredControlsRequiringControlState != null) && (this._registeredControlsRequiringControlState.Count > 0))
{
dictionary = new HybridDictionary(this._registeredControlsRequiringControlState.Count + 1);
foreach (Control control in (IEnumerable) this._registeredControlsRequiringControlState)
{
object obj2 = control.SaveControlStateInternal();
if ((dictionary[control.UniqueID] == null) && (obj2 != null))
{
dictionary.Add(control.UniqueID, obj2);
}
}
}
if ((this._registeredControlsThatRequirePostBack != null) && (this._registeredControlsThatRequirePostBack.Count > 0))
{
if (dictionary == null)
{
dictionary = new HybridDictionary();
}
dictionary.Add("__ControlsRequirePostBackKey__", this._registeredControlsThatRequirePostBack);
}
if ((dictionary != null) && (dictionary.Count > 0))
{
state.First = dictionary;
}
這段代碼是用來讀取頁面所有的控制項狀態(最後再說這個)的,foreach (Control control in (IEnumerable) this._registeredControlsRequiringControlState)
將頁面上所有註冊要使用控制項狀態的控制項進行遍曆,然後
object obj2 = control.SaveControlStateInternal();
if ((dictionary[control.UniqueID] == null) && (obj2 != null))
{
dictionary.Add(control.UniqueID, obj2);
}
將控制項狀態依次通過SaveControlStateInternal()讀出來根據控制項的唯一ID儲存在一個散列集合dictionary裡面,最後作為上面提到的Pair的First
繼續深入那麼在SaveControlStateInternal()裡面又做了什麼呢?
internal object SaveControlStateInternal()
{
object x = this.SaveControlState();
object y = null;
if (this._adapter != null)
{
y = this._adapter.SaveAdapterControlState();
}
if ((x == null) && (y == null))
{
return null;
}
return new Pair(x, y);
}
這個函數做了兩件事,首先通過SaveControlState讀取控制項狀態,但是SaveControlState是個虛擬空函數,而且你會發現裡面就一句話retrun null,這說明如果要使用控制項狀態,控制項狀態的值從哪兒來得我們自己來實現(最後再說),接下來this._adapter != null如果控制項適配器不為空白,SaveControlStateInternal還使用了_adapter.SaveAdapterControlState在適配器類的角度來儲存控制項狀態的邏輯,如果控制項指定了適配器要自己實現相應的控制項狀態讀取邏輯,最後根據X,Y的取值情況返回一個null或者Pair
接下來回到SaveAllState
Pair pair2 = new Pair(this.GetTypeHashCode().ToString(NumberFormatInfo.InvariantInfo), base.SaveViewStateRecursive());
if (this.Context.TraceIsEnabled)
{
int viewstateSize = 0;
if (pair2.Second is Pair)
{
viewstateSize = base.EstimateStateSize(((Pair) pair2.Second).First);
}
else if (pair2.Second is Triplet)
{
viewstateSize = base.EstimateStateSize(((Triplet) pair2.Second).First);
}
this.Trace.AddControlStateSize(this.UniqueID, viewstateSize, (dictionary == null) ? 0 : base.EstimateStateSize(dictionary[this.UniqueID]));
}
state.Second = pair2;
這段代碼就是在閱讀檢視狀態,並且計算視圖資訊的大小,其中最核心的就是base.SaveViewStateRecursive(),這個函數會遞迴讀取頁面控制項樹的視圖資訊,請看:
internal object SaveViewStateRecursive()
{
if (!this.flags[4])
{
object y = null;
if (this._adapter != null)
{
y = this._adapter.SaveAdapterViewState();
}
object x = this.SaveViewState();
ArrayList z = null;
if (this.HasControls())
{
ControlCollection controls = this._occasionalFields.Controls;
int count = controls.Count;
bool loadViewStateByID = this.LoadViewStateByID;
for (int i = 0; i < count; i++)
{
Control control = controls[i];
object obj4 = control.SaveViewStateRecursive();
if (obj4 != null)
{
if (z == null)
{
z = new ArrayList(count);
}
if (loadViewStateByID)
{
control.EnsureID();
z.Add(control.ID);
}
else
{
z.Add(i);
}
z.Add(obj4);
}
}
}
if (this._adapter != null)
{
if (((x != null) || (y != null)) || (z != null))
{
return new Triplet(x, y, z);
}
}
else if ((x != null) || (z != null))
{
return new Pair(x, z);
}
}
return null;
}
此函數首先如果控制項適配器不為空白那麼用this._adapter.SaveAdapterViewState()從適配器的角度閱讀檢視資訊,然後this.SaveViewState()一看便知是在讀取控制項本身的視圖資訊:
protected virtual object SaveViewState()
{
if (this.flags[0x20])
{
this.ViewState["Visible"] = !this.flags[0x10];
}
if (this._viewState != null)
{
return this._viewState.SaveViewState();
}
return null;
}
this._viewState就是我們平時使用的ViewState集合,對他使用SaveViewState()將會把ViewState的每個值封裝為一個ArrayList返回
回到SaveViewStateRecursive
ArrayList z = null;
if (this.HasControls())
{
ControlCollection controls = this._occasionalFields.Controls;
int count = controls.Count;
bool loadViewStateByID = this.LoadViewStateByID;
for (int i = 0; i < count; i++)
{
Control control = controls[i];
object obj4 = control.SaveViewStateRecursive();
if (obj4 != null)
{
if (z == null)
{
z = new ArrayList(count);
}
if (loadViewStateByID)
{
control.EnsureID();
z.Add(control.ID);
}
else
{
z.Add(i);
}
z.Add(obj4);
}
}
}
這是SaveViewStateRecursive裡很重要的一個環節,即遞迴讀取子控制項的視圖,HasControls可以判斷該控制項是否包含子控制項,如果沒有自然就不用遞迴了,ControlCollection controls = this._occasionalFields.Controls;會返回控制項的子控制項集合,然後依次對子控制項遞迴調用object obj4 = control.SaveViewStateRecursive()將子控制項視圖返回為obj4,然後將子控制項視圖放入一個數組列表z,需要說明的是數組列表中控制項的視圖和控制項本身是一對一對存在的,其中表示控制項的值可以是控制項的ID,也可以是控制項在控制項樹中的索引:
if (loadViewStateByID)
{
control.EnsureID();
z.Add(control.ID);
}
else
{
z.Add(i);
}
z.Add(obj4);
如上所示先將控制項的ID或索引放入z,緊接著再將控制項的視圖放入z
最後:
if (this._adapter != null)
{
if (((x != null) || (y != null)) || (z != null))
{
return new Triplet(x, y, z);
}
}
else if ((x != null) || (z != null))
{
return new Pair(x, z);
}
如果控制項適配器、控制項自己、控制項的子控制項都取到視圖值就將這三個元素的視圖封裝成一個Triplet對象(類似Pair只不過有三個object成員),如果沒有取到適配器的視圖就將控制項自己的視圖和子控制項的視圖封裝成Pair返回,不管以哪種形式返回視圖,我們心中都要有這麼個概念,返回的視圖資訊是樹狀結構,其結構是根據控制項樹來遞迴確定的。
回到SaveAllState
最後將控制項狀態和視圖封裝成一個Pair對象state
state.First = dictionary;
//..........
state.Second = pair2;
this.SavePageStateToPersistenceMedium(state);
再調用SavePageStateToPersistenceMedium(state)
protected internal virtual void SavePageStateToPersistenceMedium(object state)
{
PageStatePersister pageStatePersister = this.PageStatePersister;
if (state is Pair)
{
Pair pair = (Pair) state;
pageStatePersister.ControlState = pair.First;
pageStatePersister.ViewState = pair.Second;
}
else
{
pageStatePersister.ViewState = state;
}
pageStatePersister.Save();
}
SavePageStateToPersistenceMedium將state的控制項狀態和視圖取出構造了一個PageStatePersister對象,這個對象有兩個很重要的屬性ControlState 和ViewState
pageStatePersister.ControlState = pair.First;
pageStatePersister.ViewState = pair.Second;
最後將pageStatePersister.Save()序列化並將序列化的資訊作為網頁上的input儲存在用戶端,整個視圖儲存的流程就完成了
儲存在頁面上的序列化資訊相信大家已經很熟悉了:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzYwOTI5OTA4ZGQrXkI1DTujGF3xmo0Bgq2iNeJ0vQ==" />
所以大家必要被name="__VIEWSTATE" id="__VIEWSTATE"迷惑了,這個input裡面不光有ViewState的值還有ControlState的值