ViewState
时间:2010-09-17 来源:liweibird
ASP.NET
该死的 ViewState !
最近一个公司内部系统,出现上传Excel正常,读取字段正常,但是导入Excel文件的表数据到SQLServer时失败。
现象很奇怪:上传Excel正常,显示字段正常,就是点击“导入”(服务器端控件)后,马上显示“网页不存在、DNS错误、检查网络配置”等信息。
调试过程:
首先,
在“导入”事件处理方法开始处设置断点,发现此方法根本没有执行!(光标未运行到此方法入口,就已经报错了)
奇怪,方法没有执行?
再在 这个页面的Page_Load()事件处理方法处设置断点,发现竟然Page_Load()也没有执行!
晕死!难道没有正常回发到服务器端?我的IE坏了?IE设置不对?
检查一遍IE各项设置,没有异常啊,上网好好的啊,在aspx页面上这个Button上加个客户端事件alter("do it !"),运行后发现客户端事件执行了的。
再次晕死!客户端正常,但是就是没有正常Post back(回发)到服务器端,到底为啥??
抽根烟。。。又检查了一次上面的操作,再次确定客户端无问题!
再抽烟。。。 。。。检查Excel文件格式,正常。只是发现,有个表有2000行数据,比以前上传、导入都成功的Excel相比,只有这点不同!试一试以前上传的文件,成功!!
爷爷的,难道2000行就不能导入?OLEDB 导入2000行到DB就不行?!不可能啊,以前导入几万行都行!
郁闷。。。
检查上传成功后(点击导入按钮前)的页面源码,哇!把这个页面另存了一下,妈妈的,18.5M !这么大啊!
查cs文件,发现上传成功后,在读取 Excel 表名和字段名的时候,已经把表的数据放进了可序列化的对象中,然后又把这个对象,放进了 ViewState[] 对象中,进而序列化成了字符串,传递给了客户端!
原来是客户端的页面文件太大了,导致的无法正常回发!!!而罪魁祸首,就是 ViewState 对象的序列化!
NND,终于找到原因了!
数据没有必要传递到客户端的啊,立即,将这个页面上所有的 ViewState[] 替换成 Session[] .(注意:Session只存在于服务器端!而ViewState[] 只保存到Client 页面上!)
再次上传、导入——成功!
(上网一查,发现,要想ViewState [] 存在于服务器端,而不存在于Client,就必须自己重新 ViewState[] 几个方法。)
下面转载一篇文章,对比下 ViewState、Session、Application 这几个常用对象。
1.Session与DataSet 互转换(不建议)操作方法:Session["sss"] = ds; //将DataSet对象保存到Session中
DataSet ds = (DataSet)Session["sss"];//强制类型转换后得到保存的dataset
2.Session与ViewState的比较
ViewState |
||
占用服务器资源 |
true |
false |
Time out |
true |
false |
存储任何.net类型和可序列化对象 |
true |
flase(只支持strings,integers,Booleans,arrays,
支持用户定义的对象,但必须是可序列化的对象。即,[Serializable] |
加重html负载 |
false |
true |
定义 viewstate
// save in ViewState
ViewState["SortOrder"] = "DESC";
// read from ViewState
string sortOrder = (string)ViewState["SortOrder"];
Session 在Asp.Net内部,有一个StateApplication来管理Session,实际上就是一个辅助进程,处理Session到期、创建的特殊请求,在收到每一次请求的时候,辅助进程就会调用状态服务器(可以通过Web.config设置不同的状态服务器)来获取Session,如果没有对应该 SessionId的Session,则会新建一个,然后绑定到上下文中(HttpContext);与Asp不同的是,Session的状态服务器有多种,目前在Asp.Net内部实现了三种:
1) InProcStateClientManager 这是传统的Session保存方式,但是还是有些细微差别
2) SqlStateClientManager 这是将Session保存到数据库方式
3) OutOfProcStateClientManager 这是将Session保存到进程外的方式
Asp.Net的Session机制有一个特点,就是处理Session的辅助进程与保存Session的状态服务器是分开的,按照MSDN的说法,有下列好处:
“因为用于会话状态的内存不在 ASP.NET 辅助进程中,所以可以实现从应用程序故障的恢复。”
“因为所有状态与辅助进程不存储在一起,您可以干净地跨多个进程对应用程序进行分区。这种分区可以显著地提高多个进程的计算机上应用程序的可用性和可缩放性。”
“因为所有状态与辅助进程不存储在一起,所以您可以跨运行于多个计算机上的多个辅助进程对应用程序进行分区。”
Asp.Net的Session机制个人观点,感觉灵活性比较好,内部实现也比较巧妙,但是实际上因为没有做过多的测试,所以应用上会不会像它说的那么美好,不敢打包票。
3.ApplicationApplication 这是Web应用程序生命期中的全局保存区,保存在Application中的数据是全局有效的;在Asp.Net中,有一个应用程序池,其中保存了数个(或数十个)应用程序实例,每一次请求都会从池中取一个实例来处理请求,在请求完毕之前,这个实例不会接受其他请求;这就出现一个问题,同一时间可能存在多个应用程序,也就是多个线程,这些线程都存在访问Application的可能,所以在对Application中的对象进行处理的时候需要考虑线程同步的问题;实际上Application对象内部实现了一个线程锁,调用它本身的Add、Remove等方法的时候会自动调用加锁和解锁的操作,但是出于性能考虑,对于直接通过索引器或其他方式得到其中的对象并进行操作的过程,Application并没有自动处理线程同步,需要利用下列类似的代码来处理:
Application.Lock();
((int)Application["Count"])++;
Application.Unlock();
值得注意的是,调用了Lock之后,如果没有显示的调用Unlock,那么在这个请求结束的时候,Application对象会自动解锁,这样防止了造成死锁的问题,但是为了代码的健壮性,调用完Lock并且修改完毕应该立即的调用Unlock方法。
Application 对象本质上就是一个Hash表,按照键值存放了对象,由于对象是全局并且存放在服务器,并且存在多线程同时访问,所以,Application里面存放的应该是访问较多,修改较少并且是全局至少大部分功能会使用的数据,例如计数器或者数据库连接串等。
在ASP.Net中Application用法与ASP是一样的,几乎是没有什么说的,但是它多了两个特别有用的事件, Application_OnBeginRequest和Application_OnEndRequest。他们的和原来的 Application_OnStart和Application_OnEnd一样是放在global文件中的(注意这个文件在ASP中名字是 global.asa,在ASP.Net中是global.asax)。
注:这个事件,写不写On是一样的。如Application_End与Application_OnEnd是一样的
Application_OnStart是在整个ASP.Net应用首先被触发的事件,也就是在一个虚拟目录中第一个ASP.Net程序执行时触发,Application_OnEnd就正好相反,在整个应用停止时被触发(通常发生在服务器被重启/关机时)。 Application_OnRequestStart和Application_OnRequestEnd则是在每一个ASP.Net程序被请求时就发生,也就是说客户访问一次一个ASP.Net程序,这两个事件就会被触发。我们可以从下面的程序看到他的应用.我们先建立一个global.asax,内容如下:
<script language="C#" runat="server">
void Application_OnBeginRequest(Object sender, EventArgs E)
{
Response.Write("Request is Starting...<br>");
}
void Application_OnEndRequest(Object sender, EventArgs E)
{
Response.Write("Request is Ending...<br>");
}
</script>
然后将其放到本虚拟目录的根目录下,然后我们随便打开一个什么aspx文件我们在global.asax中定义的语句Request is Starting...和Request is Ending...这个不是我们在这个文件中独加的,我们将会再任何一个ASP.Net文件中看到它的影子。
ViewStateViewState 这是我们今天重点讨论的;实际上ViewState并不神秘,就是一个Hidden字段,但是它是服务器控件状态保存的基础;不熟悉的朋友可以用IE查看 Html源码,找到一个名为"__VIEWSTATE"的Hidden字段,其中有一大堆乱七八糟的字符,这就是页面的ViewState。
做过Web程序的人可能都有这种痛苦的体会,有时候为了处理页面上面比较复杂的功能,常常会加很多Hidden,然后在服务器端用一大堆判断来分析目前的状态,写起来烦人,写完了代码更是难看;实际上,ViewState就是帮我们系统的实现了保存控件状态的功能,服务器端控件能够在多次请求间保存状态也全靠它。
好,介绍就到这里,今天我们不是讨论ViewState的使用,而是从内部来探探这个东西的本质。
我们首先建一个测试的页面:
<%@ Page language="c#" Codebehind="ViewStateTest.aspx.cs" AutoEventWireup="false" Inherits="CsdnTest.ViewStateTest" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html>
<head>
<title>ViewStateTest</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio 7.0">
<meta name="CODE_LANGUAGE" Content="C#">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="