ViewState实现和优化

在ASP.NET中,我们经常用到的ViewState其实是Control里的一个属性(StageBag).

代码
private StateBag _viewState;
[WebSysDescription(
"Control_State"), Browsable(false), Designer Serializa
tionVisibility(DesignerSerializationVisibility.Hidden)]
protected virtual StateBag ViewState
{
  
get
  {
   
if (this._viewState == null)
   {
      
this._viewState = new StateBag(this.ViewStateIgnoresCase);
      
if (this.IsTrackingViewState)
      {
        
this._viewState.TrackViewState();
      }
   }
   
return this._viewState;
  }
}

StageBag实现了IstageManager,IDictionary,ICollection,IEnumerable. 从此看出它就是一个处理集合的类,主要为视图对象的存储集合以及对集合的快速检索提供支持.当我们用到ViewState时,就默认用到了IstageManager。

系统类有些类型继承了IStateManager,比如Style,这样我们可以直接拿来使用,还有它的一些派生类TableItemStyle,TableStyle,PanelStyle 都可以直接在控件开发中使用。

当我们用到单独(没有继承其他类,或者其他类没有实现IStageManager)复杂属性时,要实现IStageManager接口. 但当基类实现了IStageManger而子类希望添加自己的属性到试图状态时,我们只需自定义LoadViewStage&SaveViewStage.其他属性返回基类。

很多情况下,如果我们禁用ViewStage,那试图状态被禁用,那怎么样保存空间正常运行必须的属性状态呢?我们可以考虑ControlState.它与ViewStage类似。所以我们可以override LoadControlState()&SaveControlState()。并在此方法里手动调用对应的LoadViewState和SaveViewState.

代码
protected override object SaveControlState()
{
Pair p 
= new Pair();
p.First 
= base.SaveViewState();
p.Second 
= ((IStateManager)FaceStyle).SaveViewState();
//… …
return p;
}
protected override void LoadControlState(object savedState)
{
if (savedState == null)
{
base.LoadViewState(null);
return;
}
else
{
base.LoadViewState(p.First);
}
}

但是一般情况下我们没必要调用整个LoadViewState来对整个复杂属性。我们可以在LoadControlState&SaveControlState仅对必须的属性进行状态保存.

如果我们想加密ViewStage,我们可以在OnPreRender调用Page.RegisterRequiresViewStateEncryption().在配合ViewStateEncryptionMode来决定机密模式.

代码

protected override void OnPreRender(EventArgs e)
{
   
this.Page.ViewStateEncryptionMode = ViewStateEncryptionMode.Auto;
   
this.Page.RegisterRequiresViewStateEncryption();
   
base.OnPreRender(e);
}

同样ControlState.将控件注册为具有持久性控件状态的控件.我们可以通过

this.Page.RegisterRequiresClearChildControlState(this);

Page.RegisterRequiresControlState(this);

清除页面状态

代码
public override void DataBind()
{
base.OnDataBinding(EventArgs.Empty);
Controls.Clear();
ClearChildViewState(); 
//清理视图状态
TrackViewState();
CreateControlHierarchy(
true);
ChildControlsCreated 
= true;
}

自定义类型转换器实现高效率序列化

当我们用到复杂属性是,我们经常用到TypeConvert.它同样可以用到优化视图状态中.由于ViewState 数据是作为序列化格式串存储的,在视图装载和保存阶段,需要进行正反序列化。这对于复杂对象是比较耗资源的,还好系统已经对一些如ArrayList 和哈希表集合等类型对象进行了优化;但对于我们自定义的类对象,在默认情况下使用.NetFramework 提供的二进制序列化功能来序列化对象,比较耗资源,解决的办法是为自定义类提供专门的类型转换器(TypeConverter),实现字符串到对象的相互转换,这种方法比默认.Net 提供的二进制序列化要节省资源。

代码
protected override object SaveViewState()
{
  Pair p 
= new Pair();
  p.First 
= base.SaveViewState();
  SolidCoordinateConverter convert 
= new SolidCoordinateConverter();
  p.Second 
= convert.ConvertTo(nullnullthis.SolidCoordinate,typeof(string));
  
for (int i = 0; i < 2; i++)
  {
    
if (p.First != null || p.Second != null)
    {
      
return p;
     }
  }
  
return null;
}
protected override void LoadViewState(object savedState)
{
   
if (savedState == null)
   {
     
base.LoadViewState(null);
     
return;
   }
   
else{
        Pair p 
= (Pair)savedState;
        
if (p == null)
       {
         
throw new ArgumentException("无效的View State 数据!");
       }
      
base.LoadViewState(p.First);
  
if (p.Second != null)
  {
    SolidCoordinateConverter convert 
= new SolidCoordinateConverter();
    
this.SolidCoordinate = convert.ConvertFrom(nullnull, p.Second) as SolidCoordinate;
  }
 }
}

页面状态性能优化策略

1. 把视图状态信息保存在服务端而非客户端我们可以修改PageStagePersister来返回SessionPageStatePersister(This)..在后台代码中重写基类Page 的PageStatePersister 属性.

代码
public partial class Page_SessionPageStatePersister : System.Web.UI.Page
{
   
protected override PageStatePersister PageStatePersister
   {
     
get
     {
        
return new SessionPageStatePersister(this);
     }
   }
}

PageStagePersister在Page类中的代码

代码
protected virtual PageStatePersister PageStatePersister
{
  
get
  {
   
if (this._persister == null)
   {
     PageAdapter pageAdapter 
= this.PageAdapter;
     
if (pageAdapter != null)
     {
       
this._persister = pageAdapter.GetStatePersister();
     }
    
if (this._persister == null)
    {
       
this._persister = new HiddenFieldPageStatePersister(this);
    }
   }
   
return this._persister;
  }
}

在上面可以看出,它用到了PageAdapter类,这个类是针对Browser文件定义的来改变整个站点行为的。它是System.Web.UI.Adapters.PageAdapter类.

所以我们可以自定义PageAdapter类来达到同样的效果,并在Browser文件中定义.

代码
public class SessionPageAdapter : System.Web.UI.Adapters.PageAdapter
{
  
public override PageStatePersister GetStatePersister()
  {
    
return new SessionPageStatePersister(this.Page);
  }
}

.Browser

代码
<browsers>
//… …
<browser refID="Default">
<controlAdapters>
<adapter controlType="System.Web.UI.Page" adapterType="SessionPageAdapter" />
</controlAdapters>
</browser>
</browsers>

 分块存储视图状态数据

如果隐藏域中的数据量过大,某些代理和防火墙将阻止对包含这些数据的页的访问。由于最大数量会随所采用的防火墙和代理的不同而不同,较大的隐藏域可能会出现偶发性问题。如果您需要存储大量的数据项,可以打开视图状态分块,这样会自动将数据分割到多个隐藏域。

ASP.NET 框架提供了MaxPageStateField Length 属性,用来获取或设置页状态字段的最大长度。其属性值表示页面状态字段的最大长度,以字节为单位。
微软官方网站以及很多文章介绍说,通过设置Page.MaxPageStateField Length 属性可以指定块的最大字节数,且MSDN 明确说明此属性是公有的,在VS 2005 和VS 2008 下测试
结果是Page 下面根本没有这个属性,所以采用在配置文件Web.Config 中实现,如下:

代码
<system.web>
<pages maxPageStateFieldLength="100" />
<system.web>

当MaxPageStateField Length 属性设置为正数时,发送到客户端浏览器的视图状态将分为多个隐藏字段,并且每个字段的值都小于在MaxPageStateField Length 属性中指定的大小;而
如果MaxPageStateField Length 属性设置为负数(默认值)则表示不应将视图状态字段分成多个块区。另外,如果将MaxPageStateField Length 设置非常小,会导致性能降低。

视图状态和控件状态的总结

1.视图状态
视图状态是ASP.NET 页框架用于保存服务器与客户端往返过程之间的页面和控件值的方法。当呈现页的HTML 形式时,需要在回发过程中保留的页的当前状态和值将被序列化为Base64 编码的字符串,并输出到视图状态的隐藏字段中,即页面中HTML 源代码中的__VIEW STATE 隐藏字段,可以存储:字符串、整数、布尔值、Array 对象、ArrayList 对象、哈希表等数据类型。
视图状态有如下优点:
(1)节省服务端资源。由于视图状态是存放到隐藏字段(在HTML 代码结构中)传送到客户端的,因此不占用服务端资源。
(2)使用方便。默认已经开启视图状态。有些场合,如果控件没有注册服务端事件或者控件没有动态属情况可以将视图状态关闭,节省网络流量,提高页面呈现速度。
(3)视图状态通过散列码校检机制和使用3DES 等加密机制来保证数据安全。
(4)自定义存储位置。在Load 和Save 方法中可以自定义其存储位置。

视图状态有如下缺点:
(1)由于其存储到页面HTML 代码结构中,因此传输数据量大时,会严重影响性能。可以用视图状态分块机制,将数据分块存储,设置MaxPageStateField Length 属性。
(2)如果视图状态被禁用,则无法保存页面状态。
(3)虽然已经通过加密,但由于其是呈现到客户端隐藏字段区域,因此容易被篡改。
2.控件状态
有时,为了让控件正常工作,需要按顺序存储控件状态数据。例如,如果编写了一个自定义控件,其中使用了不同的选项卡来显示不同的信息,如TabTrip,FormView 等控件,为
了让自定义控件按预期的方式工作,该控件需要知道在往返行程之间选择了哪个选项卡。可以使用ViewState 属性来达到这一目的,然而,开发人员可以在页级别关闭视图状态,从而使
控件无法正常工作。为了解决此问题, ASP.NET 2.0 增加了一项新的存储功能:控件状态。ControlState 属性
允许您保持特定于某个控件的属性信息,且不能像ViewState 属性那样被关闭。简单地说,当禁用视图状态时, ControlState 能够完成ViewState 不能够完成的任务。
控件状态有如下优点:
(1)节省服务端资源。跟视图状态一样,控件状态存储在隐藏字段中,也不占用服务器资源。

(2)比视图状态更可靠。控件状态功能推出的一个重大原因就是,当视图状态被禁用时,它依然可以有效。
(3)自定义存储位置。在Load 和Save 方法中可以自定义其存储位置。

控件状态有如下缺点:
(1)由于其存储到页面HTML 代码结构中,因此传输数据量大时,会严重影响性能。
(2)视图状态可以用System.Web.UI.StateBag 类型的ViewState 来存储,也可以自定义编程。控件状态只能自定义编程。

posted on 2009-11-29 12:19  博览潇湘  阅读(581)  评论(0)    收藏  举报

导航