`
leonardleonard
  • 浏览: 801705 次
社区版块
存档分类
最新评论

ASP.Net ViewState的实现

阅读更多
  选择自 timmy3310 的 Blog
ViewState是.Net中提出的状态保存的一种新途径(实际上也是老瓶装新酒);我们知道,传统的Web程序保存状态的方式有这样几种:
  1、Application 这是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里面存放的应该是访问较多,修改较少并且是全局至少大部分功能会使用的数据,例如计数器或者数据库连接串等。

  2、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机制个人观点,感觉灵活性比较好,内部实现也比较巧妙,但是实际上因为没有做过多的测试,所以应用上会不会像它说的那么美好,不敢打包票。有机会,我会单独写篇文章来深入的探讨Asp.Net 内部的Session机制。

  3、Cookie  这个没甚么好说,实际上Asp.Net与Asp的Cookie没甚么分别,也许这项技术毁誉参半,而且比较依赖客户机实现,MS也没什么改进的。

  4、ViewState 这是我们今天重点讨论的;实际上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="http://schemas.microsoft.com/intellisense/ie5">
    </head>
 <body>
  <form id="ViewStateTest" method="post" runat="server">
  <asp:Button ID="btnPostBack" Runat="server" Text="Post Back" Width="85px"></asp:Button>
  <br/>
  <asp:CheckBox ID="chkTest" Runat="server" Text="This is a check box"></asp:CheckBox>
  </form>
 </body>
  </html>

   这是用Vs.Net设计出来的一个简单的页面,里面包含了一个服务器端的按钮和一个CheckBox,然后我们在服务器端响应按钮的事件:

   private void btnPostBack_Click(object sender, System.EventArgs e)
   {
 [1] Response.Write( "ViewState :"+Request.Params["__VIEWSTATE"]+"<br/>" );
  
 [2] string decodeValue = Encoding.UTF8.GetString( Convert.FromBase64String( Request.Params["__VIEWSTATE"] ) );
    
 [3] Response.Write( "ViewState decode :"+decodeValue+"<br/>" );

 [4] object viewstate = (new LosFormatter()).Deserialize( Request.Params["__VIEWSTATE"] );
 
 [5] Response.Write( "ViewState Object :"+viewstate.GetType().Name );
   }

   为了方便看,我加上了行号;第一行我们把ViewState的值打出来,第二行是什么呢?实际上ViewState保存到客户端的一串字符串就是内部的ViewState通过某种方式序列化之后再经过Base64编码得来的,所以我们把Base64编码的字符串反编码一次再打出来;至于第四行,我先不说,先看执行结果:
   运行之后,页面上什么都没有,除了按钮和CheckBox(废话 :)),我们点击按钮,然后结果如下:

  [A]   ViewState :dDwxMjU2MDI5MTA3OztsPGNoa1Rlc3Q7Pj6Gg0Qzm+7gacYWcy0hnRCT9toOdA==
  [B]   ViewState decode:t<1256029107;;l>D3i s-! t
  [C]   ViewState Object :Triplet

   然后我们来分析这个结果,A中显示的就是ViewState传到客户端的值,B中显示的是通过Base64反编码之后的值,从这里面好像还是看不出什么,C中出现了一个:Triplet ?这是什么呢,我们回到上面的代码:
      object viewstate = (new LosFormatter()).Deserialize( Request.Params["__VIEWSTATE"] );

   注意我们使用了一个LosFormatter类,实际上这个类就是Asp.Net内部为ViewState提供序列化的类,它有两个方法,一个是Serialize,就是序列化一个对象,一个是Deserialize,是反序列化,我们这里使用了反序列化的方法来把ViewState直接反序列化成一个对象,然后把这个对象的类型打出来,这个对象就是:Triplet类型,实际上Asp.Net中页面保存的ViewState就是这个类型,我们先分析一下LosFormater,再来细说.
   我们再回来看打出来的结果B:t<1256029107;;l>D3i s-! 
t,实际上通过查看LosFormatter反编译后的代码,大致上可以看出它序列化的方式是很简单的,就是判断要序列化对象的类型,如果不是直接序列化的类型,则把它的类型记录下来,然后在递归序列化它的属性,我们看B中的"t"就是表示Triplet这个类型,这个类型有三个属性,这三个属性包含在"<"和">"之间,用";"分割,而最后面的D3i s-! 
t据我分析应该是一个防止ViewState被改变的Hash值,这个不是很确定,因为反编译的代码实在是很难看,我只是了解之后就没仔细看了。

   我们刚刚分析出来Page中的ViewState反序列化之后是Triplet这个类型,实际上这个类在MSDN中就查得到,它就是一个包含了三个对象的对象,说简单点,它就是一个能放三个箱子的大箱子(好像还是说的比较糊涂,呵呵),它有三个属性:First、Second、Thrid :),分别代表三个对象。
   对应到Page当中,First是Page.GetTypeHashCode()的返回值,这个方法是System.Web.UI.Page定义的一个保护的虚拟方法,返回一个整型,由Aspx文件生成的类来实现的,因为这个类是有Asp.Net负责在运行期生成源代码并编译,它会计算出一个大常量作为返回值,这个返回值在整个Web应用程序所有的Page中是唯一的。(提一句题外话,Asp.Net自动产生的源代码可以到 系统盘:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\Temporary ASP.NET Files下面去找),这个唯一的Hash值是为了在ViewState中产生一个标记,使这个ViewState只适用与对应的页面。
   Second则是通Control.SaveViewStateRecursive方法递归保存页面控件树的ViewState返回的对象,也就是真正的ViewState的数据。
   Third中保存的是当前页面需要PostBack的控件名的列表。
  

   分析了页面的ViewState的构成,我们再来看Control的ViewState的实现。ViewState是System.Web.UI.Control类实现的一个属性,这个属性的类型是System.Web.UI.StateBag,这个类就包含了ViewState数据结构的实现,实际上它的内部也就是个Hash表,通过Key值来保存和检索数据。
  那么服务器控件是怎么实现保存状态的呢?
  我们知道,所有的服务器控件都是从System.Web.UI.Control派生的,所以都拥有ViewState这个属性,在Control内部,定义了两个Protected的虚拟方法:
    protected virtual object SaveViewState()
  和
protected virtual void LoadViewState(object savedState)

  这两个方法是给子控件派生用来保存和读取自己的ViewState的,比如我们有一个自己写的控件,往ViewState中保存了一个字符串,那么我们的方法大致像这样:
    protected virtual object SaveViewState()
    {
       object[] states = new object[2];
       states[0] = base.SaveViewState();      //记得保存父控件的ViewState
       states[1] = "Hello,I'm timmy!";        //这里保存我们自己的
     
       return states; //返回重新包装后的保存对象
    }
    获取的时候:
    protected override void LoadViewState(object savedState)  //这里的savedState就是我们Save的时候return 的object数组
    {
       object[] states = (object[])savedState;
       base.LoadViewState( states[0] );   //把父类的数据给他自己去解析
       string myData = (string)states[1];  //获取我们自己的数据
    }
   我们可以按照自己的方式来保存,不一定非要像上面这样用数组,实际上我们可以用任何支持序列化的对象都可以,父类并不关心子类如何保存,我们只要在Save和Load的时候使用同样的方式,并且把正确的数据传递给父类方法就可以了。
  
   另外,还有一个问题就是我们使用的Control的ViewState是Key-Value这样的键值对,那它是怎么保存的呢?
   实际上很简单,System.UI.Web下面有一个类叫Pair,呵呵,这个和Triplet差不多,只是它里面只有两个对象。StateBag保存的时候,First会存放所有Key值的数组,Second则存放所有Value的数组。

   到现在,我们了解了ViewState是如何序列化并且保存到客户端,也了解了控件怎么保存自己的ViewState,那么这二者是怎么结合的呢?

也就是整个页面的控件树的ViewState是怎么保存和读取的呢?

   在Control内部有两个internal的方法:
   internal object SaveViewStateRecursive();
   internal void LoadRecursive();
   这两个方法由System.Web.UI.Page来调用,Page在Render结束后就会调用SavePageViewState方法,SavePageViewState方法会调用Control的SaveViewStateRecursive()方法,这个方法就是通过递归调用每一个Control.Controls的SaveViewStateRecursive方法来保存控件树中所有控件的ViewState。到这里,可能聪明的朋友要问了,既然SaveViewStateRecursive是递归调用保存的方法,那么我们上面写的SaveViewState()方法又有什么用呢?
   我们知道,Control.Controls可能会有很多个,而且我们的SaveViewState()只保存了当前控件的数据,而没有记录控件树的结构,那么如果我们递归SaveViewState()方法来保存数据的话,那么控件树的结构就会丢失,那么Load的时候就没办法还原了,实际上在SaveViewStateRecursive方法中大致的代码是这样:
    [1] 获取控件自己的ViewState(调用SaveViewState方法)
    [2] 循环子控件
     {
         定义两个动态数组,一个保存控件的索引,一个保存递归调用子控件SaveViewStateRecursive方法返回的值
     }
    [3] 定义一个Triplet(呵呵,这个东西又出现了)
    [4] First保存本控件的ViewState
    [5] Second保存子控件的索引
    [6] Third保存递归子控件SaveViewStateRecursive方法的返回值
    [7] 返回Triplet
   这样就保存了整个控件树的ViewState和控件树的结构

   Load的方式与Save差不多,只是Load的时候会从savedState中获取子控件的索引来依次递归子控件的LoadRecursive()方法,这样才能保证正确的把保存的数据传给子控件。


   到这里,ViewState的实现我们大致了解了一下,最后得出一些结论:
   1、ViewState是存放在客户端,因此会减轻服务器的负担,是一种比较好的保存数据的方式。
   2、因为ViewState本身的限制,只能保存可以序列化的对象,而且最好不要放太多东西,能省则省,以免在减慢传输的速度,以及加重服务器解析的负担。
   3、我们通过很简单的方式就可以把ViewState里面的值获取出来,我们上面讨论了一些,虽然没有把解析的代码写出来,但是利用LosFormatter可以得到ViewState反序列化后的对象,那么要解析出来简直是易如反掌;所以ViewState在安全性上面还是比较差,建议不要
存放比较机密和敏感的信息,尽管ViewState可以加密,但是由于ViewState要保存在客户端,天生就有安全性的隐患。
   4、实际从技术角度,ViewState没有任何新意,但是结合服务器控件的设计还是很巧妙的。
  
   最后,以我个人的观点,我觉得ViewState的出现很大程度上减轻了程序员的负担,但是要看清的是ViewState的本质,合理的应用它。

   匆忙写就难免有很多问题,还希望大家多提意见,不足之处请多指教!

分享到:
评论

相关推荐

    ASP.NET ViewState 初探

    ASP.NET ViewState 是一种机制,主要用于在Web应用的页面往返行程中保持用户界面(UI)的状态。由于Web的本质是无状态的,每次用户请求页面时,服务器都会创建一个新的页面实例,而ASP.NET页面也不例外。这就意味着...

    ASP.NET ViewState

    ASP.NET ViewState是一个重要的概念,它是ASP.NET框架为了保持页面在客户端和服务器端之间交互时的状态而引入的一种机制。状态管理是Web开发中的一个关键问题,因为HTTP协议本身是无状态的,每次请求都是独立的。...

    .net ViewState解码器

    ViewState是ASP.NET中的一种机制,用于在Web表单的多个Postback之间持久化控件的状态。它是.NET框架为保持Web应用程序状态而设计的关键组件。在ASP.NET Web表单的生命周期中,当用户提交表单时,ViewState数据会被...

    理解ASP.NET的ViewState

    ### 理解ASP.NET的ViewState #### 引言 ASP.NET ViewState是ASP.NET网页用于在PostBack(页面提交)之间持久化Web表单状态的一种技术。对于许多ASP.NET开发者而言,ViewState一直是令人困惑的主题之一。无论是创建...

    ASP.NET实现九九乘法表

    此外,对于初学者,理解ASP.NET的视图状态(ViewState)和页面状态(PageState)也很重要。在九九乘法表的例子中,虽然这些状态管理机制可能不直接涉及,但它们是ASP.NET框架中处理页面间数据持久化的关键概念。学习...

    ASP.NET实现的论坛BBS系统

    7. **状态管理**:ASP.NET提供了多种状态管理方法,如ViewState、Session和Cookie,用于在用户浏览论坛的不同页面时保持数据的一致性。 8. **异常处理**:BBS系统应具备良好的错误处理机制,ASP.NET的try-catch-...

    asp.net实现的简单留言板

    6. 页面间通信:ASP.NET的ViewState或Session机制可以用来在页面间传递信息,例如保持用户登录状态,或者在显示留言列表时回显用户之前的输入。 7. 错误处理和安全性:为了提供健壮的应用,需要考虑错误处理和安全...

    ASP.net压缩ViewState,ASP.net操作EXCEL,Word,ASP.net获得验证码,汉字验证码、扭曲验证码

    在这个主题中,我们主要关注五个关键知识点:ASP.NET中ViewState的压缩、操作Excel和Word、以及生成验证码,包括汉字验证码和扭曲验证码。 首先,我们来探讨ASP.NET中的ViewState压缩。ViewState是ASP.NET页面状态...

    asp.net viewstat 查看器

    ASP.NET视图状态(ViewState)是ASP.NET框架中一个关键的概念,它用于在页面的多个生命周期之间持久化控件的状态信息。当一个Web应用程序在服务器端处理请求并回发到客户端时,ViewState允许数据在往返行程中保持...

    ASP.NET写的轮播图代码页面

    5. **状态管理**:ASP.NET提供了ViewState和Session等状态管理机制,可能用于保存轮播图的当前索引或用户设置,以便在页面间保持状态。 6. **AJAX更新**:为了实现无刷新的轮播,开发者可能会使用ASP.NET AJAX ...

    ViewState 代码查看器

    ViewState是ASP.NET框架中一种内置的状态管理机制,用于在页面回发过程中持久化控件的状态。它是一个服务器端的机制,允许Web表单在多个HTTP请求之间保持数据,而无需在客户端存储或重新发送这些数据。`ViewState ...

    asp.net在线聊天室 vb版 实现私聊 在线成员显示

    3. **ViewState**:ASP.NET中的ViewState机制用于在页面之间保持控件的状态,这对于聊天室的私聊功能至关重要,因为它能确保在用户切换聊天伙伴时仍能保留他们的聊天记录。 4. **Session和Application状态管理**:...

    ASP.NET制作的一个在线计算器

    本项目是一个使用ASP.NET技术实现的在线计算器,对于想要学习ASP.NET的初学者来说,这是一个很好的实践案例。 在ASP.NET中,我们可以使用C#或VB.NET作为后端编程语言,通过编写服务器端代码来处理用户请求和生成...

    运用ASP.NET编程实现购物车

    ASP.NET提供了多种状态管理机制,如视图状态(ViewState)、隐藏字段、Cookie或Session。在这个案例中,Session可能是最合适的,因为它可以在整个会话期间保存数据,而不会随着页面刷新丢失。 2. **控件和数据绑定**...

    ASP.NET完整实现查询前台、后台视频

    在ASP.NET中,这可以通过Session、ViewState或Cookie等机制来存储用户信息,然后在后台根据这些信息动态生成对应的菜单结构。这涉及到对用户角色管理和权限控制的理解,以及对服务器控件的熟练运用。 在实际项目中...

    ASP.NET实现简易计算器

    在这个“ASP.NET实现简易计算器”的项目中,我们主要关注的是如何利用C#语言在ASP.NET的Web表单(WebForm)环境中创建一个基本的计算器应用。下面将详细阐述相关知识点。 1. ASP.NET Web Forms: ASP.NET Web ...

    asp.net购物车实现视频代码(1)

    10. **状态管理**:在ASP.NET中,理解不同状态管理选项(如Viewstate、Controlstate、Querystring、Hiddenfield等)的使用场景和限制非常重要,特别是对于购物车这样的应用。 通过这节视频课程,开发者可以学习到从...

    ASP.NET实现投票系统

    4. **状态管理**:由于HTTP协议无状态,ASP.NET提供了多种状态管理机制,如视图状态(ViewState)、隐藏字段、Cookie和Session,以保持用户交互的数据。在投票系统中,可能需要使用这些机制来跟踪用户的投票选择,...

    asp.net页面中的viewstate内容解析器

    在ASP.NET中,ViewState是一种关键机制,用于在页面之间持久化控件的状态。当用户与网页交互时,比如填写表单或点击按钮,ViewState会存储这些交互信息,以便在后续的HTTP请求中恢复这些状态。 ViewState的内容是...

    asp.net viewstate 回发机制

    ASP.NET视图状态(ViewState)是.NET框架中用于在页面回发过程中保持用户界面状态的一种机制。在Web开发中,由于HTTP协议的无状态性,每次客户端与服务器之间的交互都会导致页面状态丢失,除非采取某种手段来存储和...

Global site tag (gtag.js) - Google Analytics