今天在做程序测试的过程中,发现程序出现只要一个客户端访问程序,其他客户端访问都受到影响。
后来一查是在程序中设了静态变量的关系,由于静态变量是全局的所以会出错。
对此问题进行学习一下:
利用asp.net开发基于B/S模式的应用系统,经常会遇到同一页面类的各函数成员之间、同一会话各页面之间、不同机器各用户页面之间的传值问题,即要解决数据共享的问题。这可以选择使用Application、Session、cookie、Static、ViewState 等方法实现。
其中Static变量的使用容易出现问题,使用不好会导致数据紊乱,给系统造成故障隐患。
用 ViewState 作为页内数据传值也是一种较好的选择 。
在C#中,static变量表示该变量属于类,而不是类的实例。可以说是该类的所有实例共享一个static变量。
asp.net的页面就是一个类,我们访问一个页面。就会在服务器上实例化一个该类的实例,来响应我们的请求。
“所有实例共享一个static变量” 这就意味着,所有的客户端访问到的asp.net页面中static变量都是同一个变量。
由于我们每次访问asp.net页面都是一个全新的对象,而不是我们上一次访问的对象。所以上次页面访问时我们对页面中变量的改动都没有保留。
遇到这个问题的时候,很多初学者的直觉就是将这个变量申明为static,自己在测试的时候发现还真的保留住了页面的状态。窃喜之余没有发现这又有引入了另外一个错误。因为你要的只是页面能保留住状态,而这个状态是针对一个客户端的(session的效果)。而得到的结果是只要一个客户端改变了该值所有的其他客户端都受到了影响(如同Applicatin的效果)。
究其原因这还要从Asp.net的运行机制谈起。在C/S模式软件开发过程中,我们通常不会关心应用程序是在哪里运行的,变量存放在哪里,客户端程序就运行在客户端,服务器端程序就运行在服务器端,一般情况下,二者除了数据库中的数据外基本没有其他共享的问题。 所以这时客户端的用户大可放心的使用static变量,因为它们就存放在客户端程序中。
于是我们就习惯的在做B/S模式的页面时也用static变量,殊不知Asp.net中的static已不同于C/S中的static。是因为在Asp.net中所有的用户将使用同一个static变量。这就意味着每一个使用该页面的用户对该变量的操作将会影响到其他用户。
解决的办法之一是可以选择Asp.net提供的ViewState对象。ViewState对象可以用来保存页面中的各种变量,甚至是对象。“有些数据可以直接保存到ViewState中,诸如字符串 、整数、布尔、数组里表、哈希表等。”
“所谓ViewState,实际上是一些键值对,ASP.NET通过它维护网页和服务器的状态,并将ViewState封装成一个或几个隐藏的表单域传递到客户端。而客户端提交时,ViewState也将被提交到服务器端。这样后续的请求可以获得上一次请求的状态。”
只要用变量名称做索引,如 ViewState[“Var”]
, 就可以存取变量Var的值,而不管Var是普通变量,还是对象,甚至是内存中的一张DataTable。服务器端会为每个连接到该页面的用户分别建立一个ViewState,所以ViewState相当于页面级的Session。相当于页面全局变量,但是一旦退出当前页面,它就会丢失。
ViewState的用法很简单,如下所示:
1、保存变量到ViewState中(赋值):
ViewState[“times”]=times; //存放普通变量times ViewState[“Orders”]=dtOrders; //存放DataTable型对象dtOrders ViewState ["aa"] ="123";
2、读出ViewState中的值(取值):
times = (int)ViewState[“times”]; dtOrders = (DataTable)ViewState[“Orders”]; string bb = ViewState["aa"].ToString();
读出变量的值时要进行强制类型转换,这是因为当变量(不管是int型的普通变量times ,还是DataTable型的对象dtOrders)被存放到ViewState中后统统按Object类型存放。所以 当我们从ViewState取出时,一定要转换成相应的类型,否则就会报错。在变量保存到ViewS tate中时,系统会自动转换。
这不是说static型变量就没用了,在C#中用static声明的类不用实例化直接使用。正是由于所有用户共享服务器端的同一个static变量,所以可以用static型对象来存取一些公用的处理模块,比如类型转换、变量验证等工作。所以要根据具体情况而定。
还有一点需要注意:如果在页面中多个过程要共享一个对象或变量,我们在页面类的开始部分定义一个页面级的全局变量是不行的,static本来可以,但上面说了这种类型的变量不安全,所以这时就可以用ViewState。
ViewState是将数据存入到页面隐藏控件里,不再占用服务器资源,因此, 我们可以将一些需要服务器"记住"的变量和对象保存到viewstate里面。viewstate并不能存储所有的.net类型数据,它仅仅支持String、Integer、Boolean、Array、ArrayList、Hashtable 以及自定义的一些类型。
ViewState 常用于保存单个用户的状态信息,生存期等于页面的生存期。viewstate是在本页面之内各函数间进行传值的 , 至于为什么要使用这种方法是因为在一个事件发生之后 , 页面可能会刷新 , 如果定义全局变量会被清零 , 所以要使用viewstate保持数据,任何事物都有两面性, 因为ViewState变量在客户端实际上是用 <input type=“hidden ” value=“ADFAIB3P234P-AFAFAF……”/>
保存的一个对象,这样如果要保存的是个对象, 甚 至是个很复杂的对象(如DataTable),这样以来就会增加网络传输的负担。 使用viewstate会增加页面html的输出量,占用更多的带宽,这一点是需要慎重考虑的。另外, 由于所有的viewstate都是存储在一个隐藏域里面,用户可以很容易的通过查看源码来看到这个经过base64编码的值,然后再经过转换就可以获取你存储其中的对象和变量值。
ViewState只能在一个页面上传值(session可跨多个页面传值),ViewState只是在当前page内有效,关了当前页,再重新打开,ViewState所保存的值也就消失了。需要在用户访问一个页面时保持一个变量的值,并随时改变它的值,用ViewState好些。ViewState是用来同步客户端与服务端的变量状态的!当有两个用户对同一页面进行操作时,若使用Static出现了数据张冠李戴的严重错误时,改为ViewState后就会一切正常了。
开发C/S模式的系统和开发B/S模式的系统有很大的不同。因此我们一定要根据不同情况正确理解各种变量的作用域和生存期,以便能够正确使用各种保存数据的方法而不至于出现错误。