ASP.NET 对象 ViewState的用途

来源:互联网 发布:京东 省市区数据库 编辑:程序博客网 时间:2024/05/18 01:35

   来公司时间不长也不短,带我们这sa也比较重视知识的学习和分享,这次让一个同事分享了一下有关viewstate,虽然没讲出什么来,却让自己发现了自己的知识处处都是漏洞.庆幸的是发现了,那么接下来的就是补漏洞了.下面就来简单的谈谈viewstate的用途.

1. ViewState就是用来存储数据的

viewstate对象以名值对的方式来存控件的值,和Hashtable的结构类似.ViewState通过String类型的数据作为索引(注意在ViewState中不允许通过整形下表的方式对其中的项进行访问,如:ViewState.Item(0)的形式是不允许的。)ViewState对应项中的值可以存储任何类型的值,实际上任何类型的值存储到ViewState中都会被装箱为Object类型。以下是几个对ViewState进行赋值的几个例子。

 

ViewState["Key1"] = 123.45M; // 存储小数

 

ViewState["Key2"] = "abc"; // 存储字符串

 

ViewState["Key3"] = DateTime.Now; // 存储时间

 

 

实际上ViewState仅仅就是一个定义在System.Web.UI.Control类中的一个保护类型(Protected)

的属性名称。由于所有服务器端的控件,用户自定义控件还有页面(Page)类都是继承自System.Web.UI.Control类,所以这些控件都具有这些属性。ViewState的真正类型实际应该是System.Web.UI.StateBag类。

严格的说,虽然StateBag类虽然定义在System.Web的命名空间下,实际上StateBag

ASP.NET并没有严格上的依存关系,它也完全可以放在System.Collections命名空间下。

事实上许多服务器端控件大多数属性值都是利用ViewState来进行数据存储。

但是你必须注意,上面的形式(通过类的私有字段)并不是大多数ASP.NET 服务器控件存储其属性值得方式。这些控件的属性值大多是通过ViewState来进行存储的。TextBox.Text属性的源代码如下:

 

public string Text 

 

{   

     get { return (string)ViewState["Text"]; }

 

     set { ViewState["Text"] = value; }

}

     大多数ASP.NET 服务器控件存储其属性值得方式是通过ViewState的方式存储的,而不是我们通常想象的那样通过类的私有字段来存储。”即便是用于设定服务器控件样式的Style类中的大多数属性值也是通过ViewState来进行存储的。所以在设计自定义的组件时,对于那些需要存储的组件属性值也最好遵循这个方式。

      就像操作HashTable一样,如果StateBag(ViewState)中没有包含某个键值的项,那么它会返回一个null。所以我们可以通过判断对应键值的项是否是null来判断某个ViewState项是否被赋值。

2. ViewState可以跟踪值的变化

     如果你设置一个控件的属性值,那么你会把ViewState中这个属性值对应的数据弄脏的。当然数据这个和数据库中的脏数据不同,这里的脏可以理解为“发生变化”的意思。你知道为什么StateBag会存在,而不会被HashTable取代吗?虽然他们都是通过名值对的方式来存储值,但是StateBag还具有对其中数据更改的跟踪过程。是否进行跟踪的开关可以被设置成开或者关,当调用StateBag.TrackViewState()方法后跟踪开关将被开启。只有在跟踪的开关设置为“开”的情况下StateBag中的数据更改才会被跟踪,只要数据出现修改,那么对应StateBag项的数据将会被标记为“脏的”(dirty)。StateBag还提供了检查一个数据项是否是脏数据的方法-- IsItemDirty(stringkey)。你也可以在不更改项数据数值的情况下将对应项设置为脏数据,这里需要使用SetItemDirty(stringkey)方法。为了说明这些,我们看一下以下的例子。这里我们假设当前的StateBag跟踪的开关是处于关闭状态的。

stateBag.IsItemDirty("key"); // returns false


stateBag["key"] = "abc";


stateBag.IsItemDirty("key"); // still returns false 


stateBag["key"] = "def";


stateBag.IsItemDirty("key"); // STILL returns false 


stateBag.TrackViewState();


stateBag.IsItemDirty("key"); // yup still returns false 


stateBag["key"] = "ghi";


stateBag.IsItemDirty("key"); // TRUE! 


stateBag.SetItemDirty("key", false);


stateBag.IsItemDirty("key"); // FALSE!

 

看到上面的例子应该很清楚了,在调用了TrackViewState()方法后,StateBag开始跟踪

其所包含项值的变化。再次无论你如何修改StateBag中项的值,都无法把数据弄“脏”

的。而且这里还需要注意一点,在TrackViewState()方法调用后,只要是出现了赋值操作

那么就会使其被标记为脏数据,StateBag并不会判断赋值前后对应项的值是否出现了变化。

如下例子所示:

 

 

stateBag["key"] = "abc";


stateBag.IsItemDirty("key"); // returns false

 

stateBag.TrackViewState();

 

stateBag["key"] = "abc";

 

stateBag.IsItemDirty("key"); // returns true

        

可能你会认为根据赋值前后ViewState是否存在变化然后再标记是否是脏数据这样更加符合常理。但是必须注意的是ViewState的项是可以存储任何类型的值的(实际上任何赋值给ViewState的变量都会被装箱为Object类型的变量),所以比较赋值前后的值是否一致实际上并没有变面上看的那么容易。而且不是每种类型都是继承IComparable的接口,所以通过调用CompareTo方法来进行比较也是不可行的。另外还有一个原因,我们知道ViewState还需要将其内部的数据进行序列和反序列化,当这些操作发生后,你得到的对象已经不是原来那个对象了,所以比较对象之间的引用也是无法完成的。基于以上这些原因,ViewState采取了一种简单的做法,也就意味着ViewState的数据变化跟踪也是一个简要的跟踪。

 

3.自动恢复数据(AUTOMATICALLYRESTORES DATA)

有些文章将这个过程和上面提到的反序列化过程混淆在一起,这样的理解是不正确的,实际上自动恢复数据的过程并不是反序列化过程的一部分。ASP.NET首先反序列化_ViewState中的值,将其还原为对象,然后再将这些还原的值重新赋值给其对应的控件。

作为所有控件包括Page类基类的System.Web.UI.Control类型中包含一个LoadViewState(objectsavedState)方法。其中需要被载入的数据就是通过参数savedState进行传递的。LoadViewState和前面所说的SaveViewState是相对应的方法。而且和SaveViewState方法类似的是,Control.LoadViewState也是简单的调用了StateBag中的LoadViewState方法。通过查看LoadViewState的源代码可以发现,这个函数实际就是将savedState中存储的名值对重新Add到StateBag列表中(StateBag.Add(key,value))。同时我们从LoadViewState也可以发现在.NET Framework 1.1中传入的object变量是一个pair类型的变量。pair类型包含两个属性First,Second都是object类型的变量,在ViewState中其中一个属性存储的是包含ViewState.Item.Key的ArrayList而另外一个属性包含的是ViewState.Item.Value的ArrayList,相对应的Key和Value在ArrayList中的下标相同。然后StateBag类就通过遍历两个ArrayList将值添加到状态项中。这里需要注意的是从LoadViewState()重新载入到ViewState的数据仅仅包含前一次请求被标记为Dirty的那些数据在载入_ViewState中包含的数据之前,对应控件的ViewState中可能已经包含了一些值了,比如那些静态控件中预先声明好的值(如:<asp:LabelText="abc"/>中的Text属性在LoadViewState()之前就已经是"abc"了)。如果LoadViewState()中需要载入的数据中已经存在值了,那么对应的值将被新值所覆盖。

这里将页面回传以后发生的事情再简单的描述一下。首先页面回传以后,整个Page将重新生成并且那些页面上声明的静态控件也都已经被解析添加到以Page为根节点的控件树中,那些静态控件对应的静态声明的属性值也都被初始化。然后是OnInit阶段,在这个阶段ASP.NET会调用TrackViewState方法,从此以后所有对控件属性的赋值操作都将导致被跟踪。接着就是LoadViewState()方法被调用,这里那些从_ViewState中反序列化出来的值将被重新赋给对应的控件,由于在此之前TrackViewState()已经被调用了,_ViewState中包含的数据对应的属性值都会被标记为Dirty。这样当调用SaveViewState的时候,这些属性值还是会被持久的保留到_ViewState中,这样在页面的一次次回传和页面一次次的重新建立的过程中,这些控件的值就被保留下来了。

0 0
原创粉丝点击