最近研究了一下Asp.net页面存储状态机制, 看了些资料, 进行一下汇总.
Web 应用程序是无状态的。每次从服务器请求网页时,都会创建网页类的一个新实例。这通
常意味着在每次往返过程中将会丢失所有与该页面及其控件关联的信息. 因此页面要维持上次请求
的页面状态, 就需要用某种方式将页面状态保存起来, Asp.net 技术是用页面状态( 视图状态和控件状
态 ), 以下都简称"页面状态". 其中控件状态为Asp.net 2.0的新特性. 在有些场合, 视图状态和控件状
态是可以通用的, 但有此场合考虑到性能及其安全等因素, 要选择具体适合此场景的页面状态存储方
式.
下面具体介绍一下页面状态的两种方式优缺点及其使用场景.
1. 视图状态.
♦ 概述: 视图状态是 ASP.NET 页框架用于保存往返过程之间的页和控件值的方法。当呈现页
的 HTML 形式时,需要在回发过程中保留的页的当前状态和值将被序列化为 Base64 编码的字符串,
并输出到视图状态的隐藏字段中, 即页面中Html源代码中的__VIEWSTATE隐藏字段. 可以存储: 字符
串/整数/布尔值/Array 对象/ArrayList 对象/哈希表等数据类型.
♦ 视图状态优点:
1. 节省服务端资源: 由于视图状态是存放到Hide字段(Html代码结构中)传送到客户端的, 因
为不占用服务端资源.
2. 使用方便: 默认已经开启视图状态(有些场合, 如控件没有注册服务端事件或者控件没有
动态属情况可以将视图状态关闭, 节省网络流量, 提高页面呈现速度).
3. 安全方面: 视图状态通过散列码校检机制和使用3DES等加密机制来保证数据安全.
4. 自定义存储位置: 在Load和Save方法中可以自定义其存储位置.
♦ 视图状态缺点:
1. 性能: 由于其存储到页面Html代码结构中, 因此传输数据量大时, 会严重影响性能.
可以用视图状态分块机制, 将数据分块存储, 设置MaxPageStateFieldLength 属性.
2. 禁用: 如果视图状态被禁用, 则无法保存页面状态.
3. 安全: 虽然已经通过加密, 但由于其是呈现到客户端隐藏字段区域, 因此容易被篡改.
2. 控件状态
♦ 概述: 有时,为了让控件正常工作,您需要按顺序存储控件状态数据。例如,如果编写了一个
自定义控件,其中使用了不同的选项卡来显示不同的信息, 如(TabTrip/FormView等控件)
为了让自定义控件按预期的方式工作,该控件需要知道在往返行程之间选择了哪个选项
卡。可以使用 ViewState 属性来实现这一目的,然而,开发人员可以在页级别关闭视图
状态,从而使控件无法正常工作。为了解决此问题,ASP.NET 2.0 增加了一项新的存储
功能: 控件状态的功能。ControlState 属性允许您保持特定于某个控件的属性信息,且不
能像 ViewState 属性那样被关闭。等一下, 下面会有详细的示例演示进行测试这一点.
简单地说, 当禁用视图状态时, ControlState能够完成ViewState不能够完成的任务.
♦ 控件状态优点:
1. 节省服务端资源: 跟视图状态一样, 控件状态存储在隐藏字段中, 也不占用服务器
资源.
2. 比视图状态更可靠: 控件状态功能推出的一重大原因就是, 当视图状态被禁用时,
它依然可以有效. (下面会有测试示例).
3. 自定义存储位置: 在Load和Save方法中可以自定义其存储位置.
♦ 控件状态缺点:
1. 性能: 由于其存储到页面Html代码结构中, 因此传输数据量大时, 会严重影响性能.
2. 自定义编程. 视图状态可以用System.Web.UI.StateBag类型的ViewState来存储使
用, 也可以自定义存储编程. 控件状态只能自定义编程.
(二) . 示例演示其用法
1) . 视图状态
代码示例使用四种不同的属性类型来演示视图状态的用法. 四种类型分别为:
a. 没有使用视图状态简单string类型属性
b. 使用视图状态的简单string类型属性
c. 自定义复杂类型FaceStyle的存储----存储复杂类FaceStyle内部属性
d. 自定义复杂类型FaceStyle的存储----存储复杂类FaceStyle继承的基类的属性
代码中注释已经比较详细, 贴出代码:
1. 自定义主控件测试类 ViewStateTest.cs 代码
2 /// Author: [ ChengKing(ZhengJian) ]
3 /// Blog: Http://blog.csdn.net/ChengKing
4 /// </summary>
5 /// <summary>
6 /// 本示例通过存储四种类型不同的属性来演示视图状态存储机制
7 /// </summary>
8 [ToolboxData("<{0}:ViewStateTest runat=server></{0}:ViewStateTest>")]
9 public class ViewStateTest : WebControl
10 {
11 private string _text;
12 [Bindable(true)]
13 [DefaultValue("")]
14 [Localizable(true)]
15 [Category("测试视图状态")]
16 [Description("没有使用视图状态存储")]
17 public string Text_NoViewState
18 {
19 get
20 {
21 return _text;
22 }
23
24 set
25 {
26 this._text = value;
27 }
28 }
29
30 [Bindable(true)]
31 [DefaultValue("")]
32 [Localizable(true)]
33 [Category("测试视图状态")]
34 [Description("使用ViewState属性来存储数据此属性")]
35 public string Text_ViewState
36 {
37 get
38 {
39 String s = (String)ViewState["Text_ViewState"];
40 return ((s == null) ? String.Empty : s);
41
42 }
43
44 set
45 {
46 ViewState["Text_ViewState"] = value;
47
48 }
49 }
50
51 private FaceStyle _faceStyle;
52 [PersistenceMode(PersistenceMode.InnerProperty)]
53 [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
54 [NotifyParentProperty(true)]
55 [Category("测试视图状态")]
56 [Description("自定义视图状态(实现IStateManager接口)存储此属性")]
57 public FaceStyle 皮肤样式_CustomViewState
58 {
59 get
60 {
61 if (_faceStyle == null)
62 {
63 _faceStyle = new FaceStyle();
64 }
65
66 if (IsTrackingViewState)
67 {
68 ((IStateManager)_faceStyle).TrackViewState();
69 }
70
71 return _faceStyle;
72 }
73 }
74
75 protected override void RenderContents(HtmlTextWriter output)
76 {
77 if (DesignMode)
78 {
79 output.Write("[自定义视图状态存储示例控件]");
80 }
81 }
82
83 protected override void CreateChildControls()
84 {
85 base.CreateChildControls();
86 }
87
88 protected override void Render(HtmlTextWriter writer)
89 {
90 base.Render(writer);
91 }
92
93 protected override object SaveViewState()
94 {
95 Pair p = new Pair();
96 p.First = base.SaveViewState();
97 p.Second = ((IStateManager)皮肤样式_CustomViewState).SaveViewState();
98 for (int i = 0; i < 2; i++)
99 {
100 if (p.First != null || p.Second != null)
101 {
102 return p;
103 }
104 }
105 return null;
106 }
107
108 protected override void LoadViewState(object savedState)
109 {
110 if (savedState == null)
111 {
112 base.LoadViewState(null);
113 return;
114 }
115 else
116 {
117 Pair p = (Pair)savedState;
118 if ( p == null )
119 {
120 throw new ArgumentException("无效的 View State 数据!");
121 }
122 base.LoadViewState(p.First);
123 if (p.Second != null)
124 {
125 ((IStateManager)皮肤样式_CustomViewState).LoadViewState(p.Second);
126 }
127 }
128 }
129 }
2. 自定义复杂类 FaceStyle.cs 代码
2 /// Author: [ ChengKing(ZhengJian) ]
3 /// Blog: Http://blog.csdn.net/ChengKing
4 /// </summary>
5 /// <summary>
6 /// 一个复杂的数据类型类定义
7 ///</summary>
8 public class FaceStyle : TableItemStyle, IStateManager
9 {
10 #region 类变量
11
12 private bool _blnOK;
13
14 #endregion
15
16 #region 构造函数
17
18 public FaceStyle()
19 {
20 _blnOK = false;
21 }
22
23 #endregion
24
25 #region 属性
26
27 [Browsable(true)]
28 [Description("自定义类测试变量")]
29 public bool OK
30 {
31 get
32 {
33 return _blnOK;
34 }
35 set
36 {
37 _blnOK = value;
38 }
39 }
40
41
42 bool IStateManager.IsTrackingViewState
43 {
44 get
45 {
46 return base.IsTrackingViewState;
47 }
48 }
49
50 #endregion
51
52 #region 方法
53
54
55 //从当前点开始, 此控件具有保存视图状态功能
56 void IStateManager.TrackViewState()
57 {
58 base.TrackViewState();
59 }
60
61
62 object IStateManager.SaveViewState()
63 {
64 object[] state = new object[2];
65 state[0] = base.SaveViewState();
66
67 state[1] = (object)OK;
68
69 //状态管理会存储此返回的值; 另外此方法返回值还有个用途: 创建复合控件时取得各个子控件的视图状态时使用
70 return state;
71 }
72
73
74 void IStateManager.LoadViewState(object state)
75 {
76 if (state == null)
77 {
78 return;
79 }
80 object[] myState = (object[])state;
81 base.LoadViewState(myState[0]);
82
83 OK = (bool)myState[1];
84 }
85
86 #endregion
87 }
3. 测试页面 Default.aspx.cs 代码
2 /// Author: [ ChengKing(ZhengJian) ]
3 /// Blog: Http://blog.csdn.net/ChengKing
4 /// </summary>
5 public partial class _Default : System.Web.UI.Page
6 {
7 protected void Page_Load(object sender, EventArgs e)
8 {
9 //禁用页面视图状态测试
10 //Page.EnableViewState = false;
11
12 //禁用控件视图状态测试
13 //this.ViewStateTest1.EnableViewState = false;
14
15 }
16 protected void btnSetProperty_Click(object sender, EventArgs e)
17 {
18 ///设置没有用视图状态存储的属性值
19 this.ViewStateTest1.Text_NoViewState = "我没有用任何视图状态存储!";
20
21 ///设置用ViewState存储的属性值
22 this.ViewStateTest1.Text_ViewState = "我是用ViewState容器存储的!";
23
24 //设置用自定义视图状态存储的属性值
25 this.ViewStateTest1.皮肤样式_CustomViewState.OK = true;
26 this.ViewStateTest1.皮肤样式_CustomViewState.BackColor = System.Drawing.Color.LightPink;
27 }
28 protected void btnRefresh_Click(object sender, EventArgs e)
29 {
30 /// <summary>
31 /// ViewState容器存储测试--测试存储ViewStateTest1.Text_NoViewState属性
32 /// </summary>
33 if (this.ViewStateTest1.Text_NoViewState == "我没有用任何视图状态存储!")
34 {
35 this.lbDisplay.Text = "this.ViewStateTest1.Text_NoViewState属性 已经保存了视图状态, :)<br><br>";
36 }
37 else
38 {
39 this.lbDisplay.Text = "this.ViewStateTest1.Text_NoViewState属性 没有保存视图状态, :(<br><br>";
40 }
41
42
43 /// <summary>
44 /// ViewState容器存储测试--测试存储ViewStateTest1.Text_ViewState属性
45 /// </summary>
46 if (this.ViewStateTest1.Text_ViewState == "我是用ViewState容器存储的!")
47 {
48 this.lbDisplay.Text += "this.ViewStateTest1.Text_ViewState属性 已经保存了视图状态, :)<br><br>";
49 }
50 else
51 {
52 this.lbDisplay.Text += "this.ViewStateTest1.Text_ViewState属性 没有保存视图状态, :(<br><br>";
53 }
54
55
56 /// <summary>
57 /// 自定义视图状态测试--测试存储类ViewStateTest1.FaceStyle的内部属性OK
58 /// </summary>
59 if (this.ViewStateTest1.皮肤样式_CustomViewState.OK == true)
60 {
61 this.lbDisplay.Text += "this.ViewStateTest1.皮肤样式_CustomViewState属性.OK 已经保存了视图状态, :)<br><br>";
62 }
63 else
64 {
65 this.lbDisplay.Text += "this.ViewStateTest1.皮肤样式_CustomViewState属性.OK 没有保存视图状态, :(<br><br>";
66 }
67
68
69 /// <summary>
70 /// 自定义视图状态测试--测试存储类ViewStateTest1.FaceStyle的基类TableItemStyle中的属性BackColor
71 /// </summary>
72 if (this.ViewStateTest1.皮肤样式_CustomViewState.BackColor == System.Drawing.Color.LightPink)
73 {
74 this.lbDisplay.Text += "this.ViewStateTest1.皮肤样式_CustomViewState.BackColor 已经保存了视图状态, 瞧,我自己的颜色就是保存的颜色, :)";
75 this.lbDisplay.BackColor = System.Drawing.Color.LightPink;
76 }
77 else
78 {
79 this.lbDisplay.Text += "this.ViewStateTest1.皮肤样式_CustomViewState.BackColor 没有保存视图状态, 瞧,我的颜色还是白色, :(";
80 }
81 }
82 }
4. 测试方案
a. 下载示例代码后, 直接Ctrl + F5运行. 点"刷新"按钮button, 这时由于没有设置给控件值,
会显示如下页面:
b. 点击另一个按钮 "设置控件属性", 然后点击刷新按钮, 刷新页面, 这里视图状态已经保存了.
会出现如下图:
可以看到, 四种不同的类型只有第一种没有被存储到视图中. 因为第一种属性类型没有启用视图.
c. 将 Default.aspx.cs中Page_Load事件中的两行代码
//禁用页面视图状态测试
//Page.EnableViewState = false;
//禁用控件视图状态测试
//this.ViewStateTest1.EnableViewState = false;
任意移除一个注释语句, 再Ctrl+F5运行, 并按步骤 b 操作, 会出现以下运行结果:
表示四种类型的属性没有被视图存储, 很显然是因为Default.aspx.cs 中Page_Load中的代码禁
用了视图功能. 所以四种属性类型没有存储. 如果当页面禁用了视图状态, 但要求其属性类型
一定要存储怎么办? 这就要用到控件状态了, 下面就演示一下控件状态的用法.
2) . 控件状态
代码中注释也比较详细, 贴出代码:
1. 自定义主控件测试类 ControlStateTest.cs 代码
2 /// Author: [ ChengKing(ZhengJian) ]
3 /// Blog: Http://blog.csdn.net/ChengKing
4 /// </summary>
5 [DefaultProperty("Text")]
6 [ToolboxData("<{0}:ControlStateTest runat=server></{0}:ControlStateTest>")]
7 public class ControlStateTest : WebControl
8 {
9 private string _Text;
10
11 [Bindable(true)]
12 [Category("Appearance")]
13 [DefaultValue(" ")]
14 [Localizable(true)]
15 [Description("测试用控件状态存储此属性值")]
16 public string Text_ControlState
17 {
18 get
19 {
20 if (_Text != null)
21 {
22 return _Text.Trim();
23 }
24 return _Text;
25 }
26
27 set
28 {
29 _Text = value;
30 }
31 }
32
33 protected override void RenderContents(HtmlTextWriter output)
34 {
35 if (DesignMode)
36 {
37 output.Write("[自定义控件状态存储示例控件]");
38 }
39 }
40
41 /// <summary>
42 /// 由于在回发事件的过程中,控件状态的注册无法在请求之间进行传递,因此使用
43 /// 控件状态的自定义服务器控件必须对每个请求调用RegisterRequiresControlState
44 /// 方法
45 /// </summary>
46 /// <param name="e"></param>
47 protected override void OnInit(EventArgs e)
48 {
49 base.OnInit(e);
50 Page.RegisterRequiresControlState(this);
51 }
52
53 protected override object SaveControlState()
54 {
55 object obj = base.SaveControlState();
56
57 if (Text_ControlState != null)
58 {
59 if (obj != null)
60 {
61 return new Pair(obj, Text_ControlState);
62 }
63 else
64 {
65 return Text_ControlState;
66 }
67 }
68 else
69 {
70 return obj;
71 }
72
73 }
74
75 protected override void LoadControlState(object savedState)
76 {
77 if (savedState != null)
78 {
79 Pair p = savedState as Pair;
80 if (p != null)
81 {
82 base.LoadControlState(p.First);
83 Text_ControlState = (string)p.Second;
84 }
85 else
86 {
87 if (savedState is string)
88 {
89 Text_ControlState = (string)savedState;
90 }
91 else
92 {
93 base.LoadControlState(savedState);
94 }
95 }
96 }
97
98 }
99 }
2. 页面测试类Default.aspx.cs代码
2 /// Author: [ ChengKing(ZhengJian) ]
3 /// Blog: Http://blog.csdn.net/ChengKing
4 /// </summary>
5 public partial class _Default : System.Web.UI.Page
6 {
7 protected void Page_Load(object sender, EventArgs e)
8 {
9 //禁用页面的视图状态
10 //Page.EnableViewState = false;
11
12 //禁用控件的视图状态
13 //this.ControlStateTest1.EnableViewState = false;
14 }
15 protected void btnSetProperty_Click(object sender, EventArgs e)
16 {
17 //设置控件的属性
18 this.ControlStateTest1.Text_ControlState = "我被设置了值";
19 }
20 protected void btnRefresh_Click(object sender, EventArgs e)
21 {
22 if (this.ControlStateTest1.Text_ControlState == "我被设置了值")
23 {
24 this.lblDisplay.Text = "this.ControlStateTest1.Text_ControlState属性 已经保存了控件状态, :)<br><br>";
25 this.lblDisplay.BackColor = System.Drawing.Color.LightPink;
26 }
27 else
28 {
29 this.lblDisplay.Text = "this.ControlStateTest1.Text_ControlState属性 没有保存控件状态, :(<br><br>";
30 }
31 }
3. 测试方案
a. 下载示例代码, 按Ctrl+F5运行, 点击 "设置控件属性", 再点击"刷新"按钮, 刷新页面, 会出现
如下页面:
表示控件状态成功存储.
b. 同样把Default.aspx.cs中Page_Load事件中的注释代码, 去掉注释
//禁用页面的视图状态
//Page.EnableViewState = false;
//禁用控件的视图状态
//this.ControlStateTest1.EnableViewState = false;
再按步骤 a 运行, 会发现如下运行结果:
控件状态仍然被存储, 到现在为止已经测试成功, 即使视图状态被禁用的情况下, 控件
状态也能够起效.
c. 把 主控件代码中的 这句: Page.RegisterRequiresControlState(this); 这句代码注释掉, 如下:
47 protected override void OnInit(EventArgs e)
48 {
49 base.OnInit(e);
50 //Page.RegisterRequiresControlState(this);//注释掉这句
51 }
再运行, 会发现控件状态会失效, 这是因为:
由于在回发事件的过程中,控件状态的注册无法在请求之间进行传递,因此使用
控件状态的自定义服务器控件必须对每个请求调用RegisterRequiresControlState
方法.
好了, 页面状态就讲这么多.
(三). 示例代码下载