ASP.NET在页面生命周期中有两个很重要的阶段分别是加载视图和保存视图,下面就设两个阶段作具体的讨论,最后讨论一下ASP.NET 2.0 引入的一个新的视图机制ConrolState
在读本文之前各位必须要清楚asp.net页面本身Page类就是个控件,所以下文中所指的控件既指Page本身也指包含在Page中的页面上的各个控件,另外本文讨论asp.net的两套视图机制一个就是大家熟悉的ViewState以下用视图二字表示,还有一套机制是asp.net 2.0新增的ConrolState以下用控件状态四个字在文中表示,请注意区分
1、视图的保存
图1
如图1 所示,视图在保存过程中会调用一个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的值