【问题标题】:ASP.NET: Compress ViewStateASP.NET:压缩 ViewState
【发布时间】:2010-03-04 14:44:16
【问题描述】:

压缩 ASP.NET ViewState 内容的最新最好的方法是什么?

这个性能怎么样?保持页面快速并最大限度地减少数据流量是否值得?

我该怎么做:

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" 
value="/wEPDwUKMTM4Mjc3NDEyOWQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgkFLGN0b
DAwJENvbnRlbnRQbGFjZUhvbGRlcl9NYWluQ29udGVudCRSYWRCdXQxBSxjdGwwMCRDb250ZW50UGxhY2VIb
2xkZXJfTWFpbkNvbnRlbnQkUmFkQnV0MQUsY3RsMDAkQ29udGVudFBsYWNlSG9sZGVyX01haW5Db250ZW50J
FJhZEJ1dDIFLGN0bDAwJENvbnRlbnRQbGFjZUhvbGRlcl9NYWluQ29udGVudCRSYWRCdXQyBSxjdGwwMCRDb
250ZW50UGxhY2VIb2xkZXJfTWFpbkNvbnRlbnQkUmFkQnV0MwUsY3RsMDAkQ29udGVudFBsYWNlSG9sZGVyX
01haW5Db250ZW50JFJhZEJ1dDQFLGN0bDAwJENvbnRlbnRQbGFjZUhvbGRlcl9NYWluQ29udGVudCRSYWRCd
XQ0BSxjdGwwMCRDb250ZW50UGxhY2VIb2xkZXJfTWFpbkNvbnRlbnQkUmFkQnV0NQUsY3RsMDAkQ29udGVud
FBsYWNlSG9sZGVyX01haW5Db250ZW50JFJhZEJ1dDXz21BS0eJ7991pzjjj4VXbs2fGBw==" />

进入这样的事情:

<input type="hidden" name="__VIEWSTATE"  id="__VIEWSTATE" 
value="/wEPDwUKMTM4Mjc3N==" />

【问题讨论】:

    标签: asp.net compression viewstate webforms


    【解决方案1】:

    简单的答案可能不是您想听到的。很多时候,页面上的控件在他们真的不需要时默认具有视图状态。在您知道自己将需要它之前关闭视图状态是个好主意,并且仅在您真正想要保持视图状态的(希望)少数情况下才打开它。

    【讨论】:

    • +1 我认为我们都为没有检查我们的网页而感到内疚。
    • +0.5,但在很多情况下 ASP.Net 控件需要 ViewState,即使它们没有正当理由需要它。例如,如果我没记错的话,如果数据网格禁用了视图状态,从 DataGrid/DataView/DataList 中的控件引发的事件将不会被正确调用(即使使用正确参数的回发仍然在客户端完成)
    【解决方案2】:
    1. 避免使用 ViewState
    2. 在 IIS 服务器上使用压缩。
    3. 您可以通过以下方式连接一些将视图状态压缩进出页面的东西:
    public abstract class PageBase : System.Web.UI.Page
    {
        private ObjectStateFormatter _formatter = new ObjectStateFormatter();
    
        private static byte[] Compress( byte[] data )
        {
                var compressedData = new MemoryStream();
                var compressStream = new GZipStream(output, CompressionMode.Compress, true);
                compressStream.Write(data, 0, data.Length);
                compressStream.Close();
                return compressedData.ToArray();
        }
        private static byte[] Uncompress( byte[] data )
        {
                var compressedData = new MemoryStream();
                input.Write(compressedData, 0, compressedData.Length);
                input.Position = 0;
                var compressStream = new GZipStream(compressedData, CompressionMode.Decompress, true);
                var uncompressedData = new MemoryStream();
                var buffer = new byte[64];
                var read = compressStream.Read(buffer, 0, buffer.Length);
    
                while (read > 0)
                {
                    uncompressedData.Write(buffer, 0, read);
                    read = compressStream.Read(buffer, 0, buffer.Length);
                }
                compressStream.Close();
                return uncompressedData.ToArray();
        }
        protected override void SavePageStateToPersistenceMedium(object viewState)
        {
            var ms = new MemoryStream();
            _formatter.Serialize(ms, viewState);
            var viewStateBytes = ms.ToArray();
            ClientScript.RegisterHiddenField("__COMPRESSED_VIEWSTATE"
                , Convert.ToBase64String( Compress(viewStateArray)) );
        }
        protected override object LoadPageStateFromPersistenceMedium()
        {
            var compressedViewState = Request.Form["__COMPRESSED_VIEWSTATE"];
            var bytes = Uncompress( Convert.FromBase64String( compressedViewState ) );
            return _formatter.Deserialize( Convert.ToBase64String( bytes ) );
        }
    }
    

    【讨论】:

    • 想必如果你使用这个你还需要删除原来的Viewstate隐藏字段?
    • 在 Save...() 方法中,我会检查 viewStateBytes 或 Compress(viewStateBytes) 是否更小,然后写入 __VIEWSTATE(未压缩)或 __COMPRESSED_VIEWSTATE。如果压缩后的版本更大,那么发送它就没有意义了。
    • @Graham - 实际上没有。您正在拦截将保存到正常隐藏 __VIEWSTATE 字段的调用,因此它将为空。
    • @Damien - 这不是一个坏主意。我很想确定是否存在显着的性能差异。如果数据足够小,压缩版本会比未压缩版本大,这也是可能的。在某些情况下,我实现了一个抽象的“EnableViewStateCompression”属性,派生页面可以覆盖该属性并在需要时设置为 false。
    • 如果您的 ViewState 是加密的,我预计不会有任何收获。
    【解决方案3】:

    我意识到这是一个旧线程,但我们已经使用 Telerik 的 RadCompression HttpModule 有一段时间了,它在压缩 ViewState、AJAX 和 Web 服务响应方面工作得非常好。您还可以在会话中作弊并保存 ViewState - 适用于低流量网站。

    http://www.telerik.com/help/aspnet-ajax/radcompression.html

    【讨论】:

    • 回答老问题永远不会错。我投了一张赞成票,也许还有一些很酷的徽章:P
    【解决方案4】:

    再次,在对此进行一些研究后,我在一篇关于 Compressing View State 的博文中总结了我的发现。

    为了保存压缩的视图状态,我这样做了:

    protected override void SavePageStateToPersistenceMedium(object state) {
        SaveCompressedPageState(state);
    }
    
    private void SaveCompressedPageState(object state) {
        byte[] viewStateBytes;
        using(MemoryStream stream = new MemoryStream()) {
            ObjectStateFormatter formatter = new ObjectStateFormatter();
            formatter.Serialize(stream, state);
            viewStateBytes = stream.ToArray();
        }
    
        byte[] compressed = CompressionHelper.Compress(viewStateBytes);
        string compressedBase64 = Convert.ToBase64String(compressed);
    
        ClientScript.RegisterHiddenField(ViewStateFieldName, compressedBase64);
    }
    

    对于加载部分,这段代码对我有用:

    protected override object LoadPageStateFromPersistenceMedium() {
        return LoadCompressedPageState();
    }
    
    private object LoadCompressedPageState() {
        string viewState = Request.Form[ViewStateFieldName];
        if(string.IsNullOrEmpty(viewState)) {
            return string.Empty;
        }
    
        byte[] decompressed = CompressionHelper.Decompress(viewState);
        string decompressedBase64 = Convert.ToBase64String(decompressed);
    
        ObjectStateFormatter formatter = new ObjectStateFormatter();
        return formatter.Deserialize(decompressedBase64);
    }
    

    【讨论】:

    • 有趣的是,通过创建您的 ObjectStateFormatter 实例,它不会获得对其使用的页面的引用,因此不会尝试加密视图状态。这样,您的序列化视图状态更容易压缩,尽管无论您的配置如何,它都不会被加密。
    【解决方案5】:

    Seb,ViewState 已经被压缩...这就是您所看到的...您的控件的压缩版本。如果您想要更少的开销,请不要使用 viewstate :)

    应将视图状态的使用保持在最低限度!

    【讨论】:

    • ViewState 仅使用 base-64 编码。除非您考虑 base-64 压缩,否则它不会“压缩”。
    • 的确如此,但他的整体观点也没有错。 Base64 的组合很难用标准算法压缩。试图通过压缩来显着节省 ViewState 是徒劳的。
    • ViewState 字符串不能轻易压缩,但不是因为它是 base64 编码的。实际上,它在编码之前就被加密了,任何加密的东西都很难压缩。您应该在加密之前添加压缩。
    【解决方案6】:

    这是您发布的视图状态的 XML 化可视化:

    <viewstate>
      <Pair>
        <Pair>
          <String>1382774129</String>
        </Pair>
      </Pair>
    </viewstate>
    <controlstate>
      <HybridDictionary>
        <DictionaryEntry>
          <String>__ControlsRequirePostBackKey__</String>
          <ArrayList>
            <String>ctl00$ContentPlaceHolder_MainContent$RadBut1</String>
            <String>ctl00$ContentPlaceHolder_MainContent$RadBut1</String>
            <String>ctl00$ContentPlaceHolder_MainContent$RadBut2</String>
            <String>ctl00$ContentPlaceHolder_MainContent$RadBut2</String>
            <String>ctl00$ContentPlaceHolder_MainContent$RadBut3</String>
            <String>ctl00$ContentPlaceHolder_MainContent$RadBut4</String>
            <String>ctl00$ContentPlaceHolder_MainContent$RadBut4</String>
            <String>ctl00$ContentPlaceHolder_MainContent$RadBut5</String>
            <String>ctl00$ContentPlaceHolder_MainContent$RadBut5</String>
          </ArrayList>
        </DictionaryEntry>
      </HybridDictionary>
    </controlstate>
    

    基本上只是几个想知道它们存在的单选按钮。 (如果未选中,浏览器不会发送带有 postdata 的 &lt;input type="radio"&gt; 字段)。这已经非常少了。

    很可能通过挂钩加载/保存方法或 HTTP 模块来压缩它,但这可能并不实际,也不是真正需要的。


    如果实际应用中的视图状态更大,请完全避免在视图状态中获取对象。这可以通过初始化OnInit()Page_Init() 方法中的控件而不是默认的Page_Load() 来实现。

    这背后的基本原理可以在 http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspxhttp://msdn.microsoft.com/en-us/library/ms972976.aspx

    快速总结:

    • ViewState 只是几乎所有控件属性(包括默认值)的后备存储。
    • OnInit()设置默认值后,将调用TrackViewState()方法。
    • 任何后续更改(例如,Page_Load())或事件处理程序都将被跟踪并提交给客户端。这样,这些控件可以在下一次请求时恢复其状态。
    • 不要依赖框架来恢复对象,而是在需要时恢复 OnInit() 中的对象。 (例如,从数据库中重新填充 DropDownList 的选项)。

    一个例外:

    如果控件被动态添加到控件树中,它会进行追赶。他们的OnInit() 方法可能会在稍后运行,最终导致这些属性最终进入视图状态。如果在OnInit() 中无法初始化控件,可以使用设置EnableViewState="false" 作为解决方法。

    每次我的视图状态意外增长时,我都会使用“ViewState Decoder 2.2”应用来找出视图状态中的结果。通常,数据并不需要在那里。

    最后一句话:

    视图状态不用于重新填充表单!! 这些值已与 postdata 一起提交。

    【讨论】:

      【解决方案7】:

      在某些情况下压缩视图状态会失败: - 如果您在页面上使用更新面板,请不要使用压缩模式。 - 如果您在 ICallBack 代码的结果中更改了视图状态,请不要使用压缩模式,因为这不会在回发时反映正确的视图状态。

      【讨论】:

        【解决方案8】:

        最小化视图状态的最好方法就是不使用它。这将导致您进行一些额外的编程工作(在回发时重新填充控制值等,但它会节省您发送到浏览器的信息量)。你不能篡改它。

        这里是 MSDN 上视图状态的链接:

        http://msdn.microsoft.com/en-us/library/ms972976.aspx

        这是一个描述一些最佳实践的链接:

        http://mnairooz.blogspot.com/2007/01/aspnet-20-viewstate-and-good-practices.html

        还有一个关于禁用 ViewState:

        http://www.codeproject.com/KB/aspnet/ASPNET_Best_Practices.aspx

        【讨论】:

        • 如果要重新填充控件,那么为什么还要首先使用 ASP.NET Web 表单抽象呢?显然,由于 Seb 将其标记为 asp.net-webforms" 这不是一个选项。
        • 您可以使用没有视图状态的 Web 表单。人们总是这样做。控件可以为您处理很多事情,甚至无需触摸视图状态。
        • 重新填充控件不是其中之一 - 这就是我们首先使用 Web 表单的原因。我认为关键是为不需要它的控件有选择地关闭它。
        • 实际上,您可以在没有视图状态的情况下重新填充控件。
        • 表单的数据可以保存在 Viewstate 中,而不需要将其存储在会话中,因此它由用户的浏览器保存,而不是服务器 - 对于“向导”或安全应用程序特别有用。
        猜你喜欢
        • 2010-10-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-02
        • 1970-01-01
        • 2016-05-21
        • 2010-09-08
        • 1970-01-01
        相关资源
        最近更新 更多