您的位置:首页 > 编程语言 > ASP

ASP.Net WebForm温故知新学习笔记:二、ViewState与UpdatePanel探秘

2014-08-11 01:48 531 查看
开篇:经历了上一篇《aspx与服务器控件探秘》后,我们了解了aspx和服务器控件背后的故事。这篇我们开始走进WebForm状态保持的一大法宝—ViewState,对其刨根究底一下。然后,再对曾经很流行的ASP.Net AJAX方案中的利器—UpdatePanel这个神奇的区域一探究竟。

一、隐藏的状态—ViewState探秘

1.1 从Http的无状态说起

  Http是一个无状态协议,同一个会话的连续两个请求互相不了解,它们由最新实例化的环境进行解析,除了应用本身可能已经存储在全局对象中的所有信息外,该环境不保存与会话有关的任何信息。另外,因为,浏览器和服务器之间是通过Socket进行通信,Http请求通常请求完毕就会关闭Socket连接,因此Http协议不会保持连接。如果保持连接会降低客户端并发处理请求数,不保持连接又会降低处理速度(建立连接所需的时间会长一点);


PS:这里我们可以这样来理解:假如我们去一个大型商场购物购买某个产品,第一次去的时候是A销售员接待了我们,带领我们来到XX产品的柜台并为我们推荐了XX产品;等我们回去使用XX产品后,觉得XX产品真心不错。第二次我们又去,但是这次却找不到上次那个A销售员了,相反商场分配了另一个B销售员来接待我们,他不知道我们上次选择了XX产品,相反它却一个劲地向我们推荐YY产品并把我们带向YY产品的柜台;这个时候,我们一般会说:我擦,把上次那个妹子给我叫来!


  基于Http协议的无状态特性,我们在ASP.Net的开发中也会经常碰到这种情况:用户上一次提交的东西,下次再提交时服务器就不记得了。很多时候,我们感到很不解?后来,我们发现原来每一次的请求服务器都开启了不同的线程来处理,也就是说每次都会new一个XXX.aspx.cs中的类对象实例来进行处理(上一次new出来为我们处理的page对象也许早就被服务器销毁了)。比如,我们在xxx.aspx.cs代码中写入了一个int类型的number成员(初始为0),每次请求我们都想让这个number自增一下,然后重新返回给浏览器。但就是这么一个简单的梦想,我们却无法轻易的实现。

public partial class AjaxCalculator : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}

protected void btnGetResult_Click(object sender, EventArgs e)
{
int number1 = Convert.ToInt32(this.txtNumber1.Text);
int number2 = Convert.ToInt32(this.txtNumber2.Text);
int result = 0;
switch(this.ddlFunc.SelectedValue)
{
case "0":
result = number1 + number2;
break;
case "1":
result = number1 - number2;
break;
case "2":
result = number1 * number2;
break;
case "3":
if(number2 == 0)
{
throw new DivideByZeroException("除数不能为0!");
}
result = number1 / number2;
break;
}
this.lblResult.Text = result.ToString();
}
}


View Code
  生成后运行该页面,可以达到以下的效果。我们输入两个数字后,选择是加法、减法、还是乘除法后,点击=按钮,即可刷新页面显示运算结果。



  在WebForm中,每一次点击runat="server"的按钮都会将调用form.submit将请求提交到服务器,服务器会返回新的页面html进行页面重绘。这是一个整页的刷新操作,不符合AJAX的风格需求。因此,我们想要将其改为AJAX版本的,除了使用基本的XMLHttpRequest外,我们还可以使用基于JQuery的AJAX方案,这些都是轻量级的原生态的AJAX技术方案。但我们伟大的微软(我哭啊,真是为我们考虑啊,连AJAX方案都为我们解决了,而且还提供了AJAX控件供我们使用,我们拖控件的习惯可以用到AJAX方案上了!!!)还为我们提供了一套叫做ASP.Net AJAX的技术方案,通过这套方案,我们可以在ASP.Net很容易地实现AJAX效果,甚至都不需要我们懂JavaScript。因此,也就出现了前些年,很多WebForm开发者陆续使用ASP.Net AJAX Extension进行AJAX开发,纷纷表示:AJAX如此简单,我等岂能不会?但是,虽然它简单易行,由于其性能问题一直被人诟病,而我们这些菜鸟也未能了解其性能问题的原因,本着知其然也知其所以然的目标,现在我们来使用它并剖析它一下。

2.2 天上掉下个林妹妹—使用UpdatePanel控件

  不得不说,UpdatePanel真的是天上掉下的林妹妹,一个神奇的控件!有了它,我们可以将页面中需要进行局部刷新的内容放到其ContentTemplate中,一个需要整页刷新的操作便可以成为局部刷新。现在,我们首先来使用其改造刚刚的简单四则计算器页面。

  (1)加入UpdatePanel,并将计算器html内容拖入ContentTemplate中

<form id="form1" runat="server">
<div align="center">
<asp:ScriptManager ID="scriptManager" runat="server">
</asp:ScriptManager>
<asp:UpdatePanel ID="updatePanel" runat="server">
<ContentTemplate>
<asp:TextBox ID="txtNumber1" runat="server"></asp:TextBox>
<asp:DropDownList ID="ddlFunc" runat="server">
<asp:ListItem Value="0">+</asp:ListItem>
<asp:ListItem Value="1">-</asp:ListItem>
<asp:ListItem Value="2">*</asp:ListItem>
<asp:ListItem Value="3">/</asp:ListItem>
</asp:DropDownList>
<asp:TextBox ID="txtNumber2" runat="server"></asp:TextBox>
<asp:Button ID="btnGetResult" runat="server" Text="=" Width="50" OnClick="btnGetResult_Click" />
<asp:Label ID="lblResult" runat="server" Text="" Font-Bold="true"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>


  (2)运行该页面,通过开发人员工具查看Http请求



  通过查看请求报文,我们了解到此次的请求响应不再是返回整页的html内容,而只是我们放在了UpdatePanel里面的html内容,页面也没有再刷新,于是不禁感叹一句:AJAX,So easy!妈妈再也不用担心我的页面了!

2.3 直到看见XmlHttpRequest才是唯一的答案—UpdatePanel原来如此

  正当我们沉浸在UpdatePanel为我们提供的神奇的AJAX世界里时,我们不禁对UpdatePanel为我们做了哪些工作产生了兴趣。

  (1)首先,我们知道AJAX的核心对象是XmlHttpRequest,那么原生态的AJAX请求的JS方法是如何写的呢?

function ajax(url, onsuccess) {
var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); //创建XMLHTTP对象,考虑兼容性。XHR
xmlhttp.open("POST", url, true); //“准备”向服务器的xx.ashx发出Post请求(GET可能会有缓存问题)。这里还没有发出请求

//AJAX是异步的,并不是等到服务器端返回才继续执行
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) //readyState == 4 表示服务器返回完成数据了。之前可能会经历2(请求已发送,正在处理中)、3(响应中已有部分数据可用了,但是服务器还没有完成响应的生成)
{
if (xmlhttp.status == 200) //如果Http状态码为200则是成功
{
onsuccess(xmlhttp.responseText);
}
else {
alert("AJAX服务器返回错误!");
}
}
}
//不要以为if (xmlhttp.readyState == 4) {在send之前执行!!!!
xmlhttp.send(); //这时才开始发送请求。并不等于服务器端返回。请求发出去了,我不等!去监听onreadystatechange吧!
}


  (2)其次,通过查看运行页面的html,我们可以发现加入UpdatePanel后,我们的html中多了这么几个js引用。



  (3)既然我们知道要发AJAX请求,必然会涉及到XmlHttpRequest。那么,我们就在这几个js中取看看是否有涉及到XmlHttpRequest。通过查看,我们找到了这样一个似曾相识的js方法:

function Sys$Net$XMLHttpExecutor$executeRequest() {
/// <summary locid="M:J#Sys.Net.XMLHttpExecutor.executeRequest" />
if (arguments.length !== 0) throw Error.parameterCount();
this._webRequest = this.get_webRequest();
if (this._started) {
throw Error.invalidOperation(String.format(Sys.Res.cannotCallOnceStarted, 'executeRequest'));
}
if (this._webRequest === null) {
throw Error.invalidOperation(Sys.Res.nullWebRequest);
}
var body = this._webRequest.get_body();
var headers = this._webRequest.get_headers();
this._xmlHttpRequest = new XMLHttpRequest();
this._xmlHttpRequest.onreadystatechange = this._onReadyStateChange;
var verb = this._webRequest.get_httpVerb();
this._xmlHttpRequest.open(verb, this._webRequest.getResolvedUrl(), true );
this._xmlHttpRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest");
if (headers) {
for (var header in headers) {
var val = headers[header];
if (typeof(val) !== "function")
this._xmlHttpRequest.setRequestHeader(header, val);
}
}
if (verb.toLowerCase() === "post") {
if ((headers === null) || !headers['Content-Type']) {
this._xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
}
if (!body) {
body = "";
}
}
var timeout = this._webRequest.get_timeout();
if (timeout > 0) {
this._timer = window.setTimeout(Function.createDelegate(this, this._onTimeout), timeout);
}
this._xmlHttpRequest.send(body);
this._started = true;
}


  由以上的方法名我们可以猜到,此方法是一个执行AJAX请求的方法。在此方法中,创建了XmlHttpRequest对象,也使用了open方法指明以GET还是POST方法向服务器哪个处理程序发送请求,并且也为该请求指定了请求成功后需要执行的回调函数方法(onreadystatechange),最后调用send方法正式发送请求

  由此,我们可以初步分析出一个结论:UpdatePanel本质还是帮我们封装了以XmlHttpRequest为核心的一系列方法帮我们将CodeBehind中的同步事件变为了异步操作,并通过DOM更新指定的HTML内容,使得我们可以方便地实现AJAX效果

  但是,我们也不由发出感叹:本来可以很简单地使用XmlHttpRequest来实现的东西,为什么使用UpdatePanel会引入这么多js,并且为我们返回的东西还是那么多(比如上面的例子,我只需要的数据是一个结果,却给我返回一部分无用的html,还有一系列的hiddenId之类的数据)。在对性能要求较高的应用场合,如果使用UpdatePanel来实现AJAX会增加服务器的负载,并且会消耗掉不必要的网络流量(比如每次请求都会来回都会发送ViewState里的数据,在性能和数据量上都会造成损失)。园子里的浪子曾经在他的博文《远离UpdatePanel带给我的噩梦》里边写到:“UpdatePanel在页面小的时候还是很好用的,而当页面控件数不断上升的时候,UpdatePanel就开始直线下降,我们现在页面有4,5百个控件,每做一次PostBack需要长达15秒钟之长,实在让人无法忍受。”

  那么,有木有方式可以替换UpdatePanel呢?其实答案很简单,那就是使用基于XmlHttpRequest的js方法,再加上一定的js回调函数即可。这就要求我们掌握javascript,不能只做拖UpdatePanel控件的程序员。现在基于js的JQuery库也早已为我们封装了XmlHttpRequest,提供了ajax开发的一系列方法供我们调用,相当于UpdatePanel的“重量级”来说,可谓是轻了不少,是一个“轻量级”的AJAX开发方式。通过借助jQuery Ajax+ashx可以方便地在.Net中进行Ajax开发,并且具有不错的性能,这也是我实习所在的企业中经常用到的方式。

三、学习总结

  本篇主要学习了WebForm中的状态保持法宝—ViewState,以及曾经的ASP.Net AJAX方案的利器—UpdatePanel,虽然一直在说这个不好,那个别用。但是,微软之所以为我们提供了这些东西,肯定有它存在的理由,并不一定都是不好的东西。所谓利器在手,没有一点内功心法的人还是使用不好它,无法发挥出其100%的优势。因此,身为.Net学习者的我们,不能满足于微软为我们所提供的便利,要知其然也知其所以然,做一个上进的程序员,加油吧!

  校园招聘的大潮就快来临,希望园子里跟我一样即将毕业的菜鸟们能够好好复习基础,在招聘中赢得一份好offer,实现自己的价值!

参考资料

  (1)杨中科,《特供2014版ASP.Net教程》,http://net.itcast.cn/subject/tegongnet/index.html

  (2)匿名未知,《状态保存机制之ViewState概述及应用》,http://www.jb51.net/article/33686.htm

  (3)popping_dancer,《ASP.Net原理篇之ViewState》,http://www.2cto.com/kf/201210/160413.html

  (4)玉开,《ASP.Net 4.0新特性-输出更纯净的Html代码》,http://www.cnblogs.com/yukaizhao/archive/2010/05/22/asp-net-new-feature-pure-html.html

  (5)Infinities Loop,《ASP.Net ViewState详解》,http://www.cnblogs.com/wwan/archive/2010/11/18/1880357.html

  (6)LifelongLearning,《ASP.Net 4.0中ViewState使用简介》,http://www.csharpwin.com/dotnetspace/13220r6527.shtml

  (7)自由飞,《禁用VIEWSTATE之后(一)》,http://www.cnblogs.com/freeflying/archive/2009/12/28/1634229.html

  (8)MSDN,《了解使用ASP.NET AJAX进行局部页面刷新》,http://msdn.microsoft.com/zh-cn/dd361854.aspx

  (9)xiaomin,《UpdatePanel工作原理》,http://www.cnblogs.com/xiaomin/archive/2011/11/01/2232215.html

  (10)浪子,《远离UpdatePanel给我的噩梦》,http://www.cnblogs.com/walkingboy/archive/2007/01/09/615691.html

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: