一寒的博客----我亦无它,唯学习尔!

让自己的技术真正的进步才是自己最大的收获

用户操作
[即时聊天] [发私信] [加为好友]
一寒ID:sdust_zh
11821次访问,排名9634(-1)好友0人,关注者0
我亦无它,唯学习尔
sdust_zh的文章
原创 35 篇
翻译 0 篇
转载 130 篇
评论 5 篇
一寒的公告
欢迎大家来到我的是博客!
最近评论
xiaoqijun:我想是浏览器地址反应的是当前页面的地址, 当A->B时, 通过Server.Transfer改变了A页面的内容,但仍是A页面的访问. 同理, 当你在B页面通过Server.Transfer将B页面的内容改变成A时, 当前页面仍是B页面的访问,所以浏览器自然就反应成B的地址.
xiaoqijun:我想是浏览器地址反应的是当前页面的地址, 当A->B时, 通过Server.Transfer改变了A页面的内容,但仍是A页面的访问. 同理, 当你在B页面通过Server.Transfer将B页面的内容改变成A时, 当前页面仍是B页面的访问,所以浏览器自然就反应成B的地址.
wpfsilverlight:推荐一个QQ群:10378186 (WPF和Silverlight的技术群), 这里面讨论气氛不错.
haoliumeihua:这里这么多的好资源啊,谢谢了
fancylea:很有道理
文章分类
收藏
相册
我的图书
CSDN
bluedusk
CSDN个人空间
jelink
xiaoxijin
叶帆工作室
孟子E章
朱少民
疯狂的程序员
祥龙之子
紫藤阁
老毕的专栏
蝈蝈俊.net
邹建
博客园
cleo
Dflying Chen
Leo——感谢生活!
nanshouyong326
Rickie Lee's blog
THIN
webabcd
亮.net
博客园-陋室铭(RSS)
天轰川
小牛哥
翔之天国
老毕的专栏
技术网站
faq频道--果果技术网<
偶要雷锋IT站
和讯
天极Yesky
网魂工作室
蓝色理想
界面设计
OperMasks.org
中国网站资源
设计路上
其它
B3 bohemia's
prowyh
Q.yuhen
TechNet
webcast
优艾网,设计
刘伟
好老师Oracle
柚枫
管理前沿
网络杂货铺
下载
10GB软件园
UJU猫宽带宝藏论坛
中国IT实验室电子书下载频道
英文专题网站
prototypejs
Standards
subbu.org
The Dojo Toolkit
存档
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 关于Asp.net中的ViewState、Session....等的总结 收藏

新一篇: SQL Profiler  | 旧一篇: 国外译文:43个你应当避免的Web设计错误

 
1.Session与DataSet互转换(不建议)操作方法:
Session["sss"] = ds;         //将DataSet对象保存到Session中

DataSet ds = (DataSet)Session["sss"];//强制类型转换后得到保存的dataset
2.Session与ViewState的比较

Session
ViewState
占用服务器资源
true
false
Time out
true
false
存储任何.net类型
true
flase(只支持strings,integers,Booleans,arrays,
ArrayList,Hashtable,customTypeConverters)
加重html负载
false
true
session对于整个application有效,而viewstate相当于某个页面的session.
定义 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="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的本质,合理的应用它。

发表于 @ 2008年07月22日 15:01:00|评论(loading...)|收藏

新一篇: SQL Profiler  | 旧一篇: 国外译文:43个你应当避免的Web设计错误

评论:没有评论。

发表评论  


当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
Csdn Blog version 3.1a
Copyright © 一寒