ASP.NET页面状态管理——ViewState的使用
2010-03-03 19:49:59|
ASP.NET ViewState设计目的是为了持久化当前页面中的对象的状态,以便下次在页面回发(Postback)后能够还原页面的状态。那么有两点需要注意:
ViewState只在需要Postback的页面里才需要使用;
在1前提之下,只有初始状态值被修改了的对象才需要持久化,即才需要使用ViewState。
1比较清楚,来谈第2点。以简单的Label控件为例,先来看一下它的Text属性的实现:
public virtual string Text
{
}
很显然Text属性的后端都是以ViewState为存储介质的,ASP.NET服务器端控件的很多属性都是以类似方式实现的。假设一个页面default.aspx里只有一个Label控件, <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>,当访问该页面时,Label控件发送到客户端浏览器的代码大概为 <span id="xxx_Label1">Label</span>,同时ViewState中也保存了一份Text属性的值,形式大概为<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTgwMzg2ODMxMQ9kF
到目前位置,ViewState并没有发挥作用(这里谈论的都是在ViewState Enable的情况下),额外的保存了Text属性的值在这里是多余的。假设该页面还有一个Button控件,点击该按钮页面回发,那么在次过程中ViewState就发挥作用了么?分析一下该页面回发过程中Label控件生命周期的某些过程。首先,回发后,Label控件依然要经历初始化阶段(Init),这个阶段要创建一个Label控件的实例,同时设置其Text属性的,因为Text属性后端是以ViewState为存储介质的,所以就相当于向ViewState里添加了一个值,接下来,因为是回发过程,所以控件还要LoadViewState,即加载前次访问该控件的状态值,下面是Label控件的实现:
protected override void LoadViewState(object savedState)
{
}
因为在前一访问过程中ViewState中所保存的Label的Text属性的状态值就是Label的初始值,所以导致了这里的LoadViewSate过程是多余的了,而且Init和LoadViewState两个过程对Text属性都赋了相同的值。由此可见,即使在页面回发中,如果不需要对属性的初始值进行修改,那么持久化属性的值(即使用ViewState)也是没有意义的。而且会带来多余的资源浪费,如两次对Text属性的赋值,以及增加了ViewSate的体积所带来的多余的网络传输。
那么,什么情况下使用ViewSate是值得的呢?我们先来把前面例子中两次访问的过程理一下:
第一次访问页面,Label控件初始化,设置Text属性的值,即向ViewState中添加了一个条目;
页面发送前(Render前),控件SaveViewState,即ViewSate中的值序列化,保存到一个隐藏域中;
页面发送,Label控件发送为相应的HTML标签,读取Text属性设置HTML标签的对象属性值,同时发送隐藏域及其值。对于Labe的Text属性来讲,相当于一份ViewState中的值发送了两份客户端拷贝;
第二次回发访问,Label控件初始化,设置Text属性的值,即向ViewState中添加了一个条目;
由于是回发访问,需经历LoadViewState过程,本例中即读取ViewState中Text属性在上一次访问中的状态值,而这个值实际上等于过程1中设置的值,读取的值再次设置Text属性,
第二次发送,重复过程2,3.
从过程1控件初始化,到过程6,第二次发送前SaveViewState,在这两个过程中间,如果不需改变Text属性的初始值,那么实际上就不需要使用ViewState。假设我们在过程1、2中间改变Text属性值,如在Page_Load中如此:
由此,可以得出,在满足前面所述的两个条件时才应该使用ViewState。那么在现实应用中同时满足以上两个条件的情况下多么,也就是说我们需要使用ViewSate的时候多么?
很显然,满足这两个条件最大宗的情况就是数据绑定。而我认为ViewState的设计目的主要就是为了将必要的信息持久化在页面中,避免在两次访问中(确切的说不只两次,而是所有的回发访问中)都要进行数据绑定(而每次数据绑定往往意味着一次次的数据库访问)。例如用GridView绑定DataSource控件展现一个类表数据,在ViewSate Enable的情况下,页面第一次加载时进行数据绑定,在随后的回发访问中,如果仍是访问当前数据视图,即没有进行分页、排序操作等,DataSource不会再进行数据绑定,因为所有的信息都可以从ViewSate中获取,不需要再次访问数据库再次绑定数据控件了。而如果你将ViewState Disable掉,那么每次访问则都需要进行数据绑定了(可以通过SqlProfiler来捕捉SqlDataSource在两种情况下对数据库的访问情况)。这个场景可能最能说明ViewSate的设计初衷了。
然而在实际的应用中,上面的这种场景多么?在数据列表页面,往往没有除了分页排序等之外的回发操作(你放Grid的页面里有回发的按钮么?),而分页,排序操作所引起的回发显然是需要数据再绑定的。如果是这种场景,那么你就应该把这个页面或者把这个Grid的EnableView属性设为false了。这里讲点题外话,有人会说如果设成false,那Grid的分页信息、排序信息怎么传递给后续的回发访问呢?其实在ASP.NET 2.0中控件的状态管理被分为了两部分view state和control state。两者的区别是什么呢,那ASP.NET 1.x中的DataGrid控件来说,DataGrid的所有状态信息都保存在view state当中,但是这些信息所符合的view state应用场景是矛盾的,比如你的页面没有回发操作,你不必把所有数据缓存到view state里,这时你会把datagrid的enableviewstate属性设为false,但当你这么做后,datagrid的另一些功能如翻页、排序,就没法使用了,因为翻页排序的状态信息也是保存在view state中的,如pageindex、sort asc/desc等。就类似于这种问题,ASP.NET 2.0中又引入了control state,control state的存储方式与view state相同,不同的地方在于它不会被disable掉。这样control state用来存储那些控件的功能性的,必需的信息。比如即使GridView的view state被禁止了,它的分页,排序等信息还是仍然正常工作的。
ASP.NET的一些设计,如整个页面一个form元素,以及本文谈到的view state的使用等,只是在宏观上,概率统计上能达到节约成本,提高效率的目的。但是具体到一个Web程序, 一个应用场景,一个页面,甚至是一个控件,如果你知到它们背后的东西,你就会更好更正确的去实现它或使用它。
=========================================================
问题如下:
-------------------------------------------------------------------------------------------------------------------------------------------------------
为什么TextBox的EnableViewState已经设置成了FALSE,Textbox在回发之后还是可以保存Textbox中的输入的内容?
-------------------------------------------------------------------------------------------------------------------------------------------------------
原因解答:
-------------------------------------------------------------------------------------------------------------------------------------------------------
一、EnableViewState优先级别:
二、经常需要变值的控件,回发的数据并不依赖于ViewState。回发的控件都实现了IPostBackDataHandler接口,该接口的LoadPostData方法中,会对返回的值和ViewState中的值进行判断,如果改变了的话,调用RaisePostDataChangedEven
-------------------------------------------------------------------------------------------------------------------------------------------------------
扩展:
在ASP.Net中对各个WebForm控件引入以前没有的EnableViewState属性。这个属性究竟有什么用。我们知道对于WebForm而言,其代码是在服务器端的,以处理客户端的请求。当用户通过浏览器浏览网页的时候,会对网页进行某些操作,比如打开新链接,或单击某个按钮。在ASP中,这些是通过脚本语言对其进行处理,之后再传递给服务器端。但是在ASP.NET下,由于采用了code behind技术,在coding的时候,通常是将以前客户端完成的工作放到了服务器端。
那么,服务器是怎么知道客户的操作的呢?比如我在文本框输入的内容,或者单击了登录按钮,服务器端是怎样得到这些信息的呢?因为没有这些信息,服务器端就无法响应客户的请求。原理就是ASP.NET引用了viewstate的机制。在服务器端保存了网页各个控件及页面的状态,这其中包括各个控件在页面上的布局,和他们各自的属性。这些值就保存在ViewState下。我们可以观察Aspx页面的html源代码,假设这个页面上有一个button按钮,和一个listBox控件,html文件如下:
<input type="hidden" name="__VIEWSTATE" value="dDwzODYzNDM5NTU7Oz7FvviJ
我们它的不同之处,一是少了以前所必须响应客户端事件的脚本语言,一是多了一个名为”_VIEWSTATE”的属性。其值是一长串字符。类型为“hidden”。这个值记录的就是各个控件和页面的状态信息。当用户对页面进行相关操作的时候,状态值发生改变,并将改变的值传递给服务器端。服务器端在比较改变后的状态值和初始值之间的区别,以响应具体的请求。
一旦页面的控件很多,这种频繁的传递控件状态值对网络的消耗是很大的,因此,ASP.Net提供了EnableViewState属性,系统默认的值为true。当设置为true时,在传递状态值时就包括该控件;如果设置为false,则传递状态值时则不包括它。既然状态值不包括该控件,则客户端对它进行的操作,服务器端是不响应的。
我们可以做个实验,在Button1_Click事件中,编写代码:
ListBox.Items.Add(”客户端点击按钮一次!”);
此时运行该应用程序,单击网页上的按钮,在ListBox中会添加内容,不断地单击,内容则不断添加。如果我们将ListBox的EnableViewState属性改为false时,不断单击按钮,则只能添加一次。
这样有什么好处呢?如果我们在开发Web应用程序时,某些控件是不需要接受用户的操作或只需要接受一次操作的时候,我们可以将这些控件的EnableViewState属性改为false,这样可以优化我们的程序,提高网络访问的速度。