在Asp.net里ms提出了一种新的状态保持,这就是ViewState,当然原来的Session和Cookies在Asp.net可以继续使用,那我们用一个Web的请求来说明一下ViewState的一个工作流程(只关心ViewState),即使不写asp.net服务器控件,但我想了解一下页面的请求和回传对写asp.net的程序还是很有好处的。
客户端向服务端请求一个新的aspx页面:
1、 服务器端会创建一个控件树,再创建页面上的所有控件,把控件加到控件树中。
2、 TrackViewState状态跟踪,检查控件的EnableViewState属性,对EnableViewState=ture的控件进行跟踪。
3、 触发页面OnLoad。
4、 预生成OnPreRender。
5、 检查控件的EnableViewState属性,对需要保存状态的调用SaveViewState[状态1]。
6、 Render生成。
7、 … …
从客户端回传一个aspx页面,页面被Post回服务端后。
A、 和上述1一样。
B、 和上述2一样。
C、 LoadViewState,加载控件提交前的状态[就是加载状态1]。
D、 LoadPostdata,从Post回来的表单中取出相应的数据,并把这些数据更新控件的状态[状态2]。
E、 和上述3一样。
F、 触发修改事件和回传事件。
G、 和上述4一样。
H、 保存状态SaveViewState[状态2]。
I、 Render。
J、 … …
上面就是ViewState的工作的一个流程,其实也就是aspx的生命周期,虽然我们在5和H对ViewState进行保存,在C进行了ViewState的装载,那又究竟是怎样保存和装载的呢?这就是ViewState的串行化和并行化的,在5和H对ViewState进行串行化,把这些状态序列化为一个字符串(如value="/wEPDwULLTE3NjgxMzM0MDIPZBYCZg9kFgICBg9kFgICAQ9kFhACAQ8PFgQeC05hdmlnYXRlVXJsBRdodHRwOi8vd3d3LmNuYmxvZ3MuY29tLx4EVGV4dAUJ5Y2a5a6i5ZutZGQCAw8PFgIfAAUdaHR0cDovL21hcHNlcnZlci5jbmJsb2dzLmNvbS9kZAIHDw8WBh4GVGFyZ2V0ZR8ABR1odHRwOi8vbWFwc2VydmVyLmNuYmxvZ3MuY29tLx8BBQltYXBzZXJ2ZXJkZAIJDw8WBB8ABRdodHRwOi8vd3d3LmNuYmxvZ3MuY29tLx4ISW1hZ2VVcmwFFn4vSW1hZ2VzL2FkbWlubG9nby5naWZkZAINDxYCHgdWaXNpYmxlZ2QCHQ9kFgICAQ9kFgQCAg8PFgIeC0N")发送到客户端,当页面再被Post回服务端的时候,在C又对这个字符串进行并行化,把它们反序列化为对象状态。aspx页面有串行化和并行化的过程,同时这两个过程的代价又是比较高昂的,所有我的原则是尽可能少的保存控件的ViewState。那什么样的对象可以被保存在ViewState中呢?原则上来讲,可以被序列化的对象都可以被保存在ViewState中,但是在asp.net中ViewState对Array、ArrayList和HashTable进行了优化。
在aspx页面的请求和回传过程中ViewState都是以明文形式传送,很容易被他人截取,对特别重要的信息可以考虑加密或者用Session等方式来保存,但这不是控件开发者的负责的,而应该是由页面开发者来完成。
最后:为什么我们需要回传的一个aspx页面都必须有一个运行Runat=”Server”的Form的,上述的D中我们看出,我们需要这个Form来回传控件的更新数据。同时我们也可以看到所有的ViewState的处理都是服务端进行的,与客户端没有任何的关系,所有不管你是ie的客户端还是firefox的客户端,都可以访问发布的asp.net站点。
本系列的第三章我们了解了ViewState的基础知识,也大概的了解了Aspx页面的生命周期,今天继续讲解ViewState的Asp.net服务器控件中的使用和复杂的ViewState管理。
ViewState的简单使用我们只要举个简单的例子即可:
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
namespace ClassLibrary1
{
public class Class1 : WebControl
{
private string _sex;
public string Sex
{
get
{
return _sex;
}
set
{
_sex = value;
}
}
public string Name
{
get
{
if (EnableViewState)
{
object o = ViewState["Name"];
if (o != null)
return (string)o;
else
return string.Empty;
}
else
return string.Empty;
}
set
{
if (EnableViewState)
ViewState["Name"] = value;
}
}
protected override void RenderChildren(System.Web.UI.HtmlTextWriter writer)
{
writer.Write("Name:" + Name + "<br>");
writer.Write("Sex:" + Sex);
}
}
}
效果如下:
一个Aspx页面在提交和返回的过程会执行LoadViewState和SaveViewState两方法,这就是控件的状态并行化和串行化的过程,而这个过程是通过LosFormatter对象来完成的。Los 格式化程序用于高度压缩 ASCII 格式的序列化,它实现了整数、bool、字符串的串行,同时Array、ArrayList和HashTable的对象图进行了优化,控件保存在ViewState中的任何对象都必须由LosFormatter来串行化。
ViewState属性的类型是StateBag类,改类是个字典,它通过实现ISateManager接口参与状态管理。StateBag保存键/值对,其中键是字符串而值是对象,当某个对象添加到StateBag实例中时,StateBag自动为改对象添加一个附加位,它来存储对象是否被更改过。Control的状态管理默认都是由ViewState来完成,但是,当控件定义了复杂属性时,想用ViewState来自动管理这些属性就不可能了,所以我们复杂属性的自定义类型可以通过IStateManager接口来参与状态管理。IStateManager的原形如下:
bool IsTrackingViewState { get; }
void LoadViewState(object state);
object SaveViewState();
void TrackViewState();
在.net中Control类没有从IStateManager继承下来,但是Control却实现IStateManager的原形,这里的实现就不在举例来说明,如果需要的话,可以参考《ASP.NET服务器控件与组件开发》的183页。