将 ASP.NET ViewState 移出 ASPX 页面以提升性能






4.37/5 (41投票s)
如何提升 ASP.NET 项目的性能,将 ViewState 保存在服务器端而不是 ASPX 页面上。
引言
ASP.NET ViewState 是一个极好的机制,它简化了 ASP.NET 开发者的工作。但是,正如大家所知,.NET Framework 将 ViewState 数据保存为 ASPX 页面上的一个隐藏字段。如果你的页面只有很少的控件,这不成问题。但是,如果你的页面包含一些 Panel
和/或 DataGrid
,通过本文演示的技术,你可以显著减少页面的加载时间。
我们将从分析框架需要在 ViewState 隐藏字段中保存数据的位置开始。答案是:只在服务器端。系统不需要在客户端处理 ViewState 数据,所以,如果我们开始将这些数据保存在服务器端,而不是在服务器和客户端之间来回传输,我们将节省大量的文档加载时间。在你安装应用程序的同一台计算机上加载页面时,你可能不会注意到差异,但是,尝试使用拨号连接进行测试,你就会明白会发生什么。你还可以查看使用此技术和不使用此技术的生成的源代码的大小。
这个例子包含一个继承自 System.Web.UI.Page
的类,并重写了 SavePageStateToPersistenceMedium
和 LoadPageStateFromPersistenceMedium
方法。这些方法负责保存和加载页面控件使用的 ViewState。我们所做的是拦截对这些方法的调用,并使用 Web.Config 中一些简单的自定义配置键来设置这个类的运行方式。它可以将 ViewState 保存在服务器端,有两个目标:Session
和 Cache
,并且能够像原始类一样工作,将 ViewState 数据保存到 ASPX 页面。
如果你已经有一个项目并希望开始使用此技术,你所需要做的就是为伴随此示例的类添加一个引用,并将你的“代码隐藏文件”从中继承,而不是直接继承 System.Web.UI.Page
,并将配置信息添加到你的 Web.Config 文件中。
为了开发这项技术,我阅读了互联网上大量的文章,但是,有一篇文章真正地“启发了我”,让我明白了如何实现以及它带来的好处。在“兴趣点”部分可以找到更多信息。
我包含了一个演示项目,你可以下载并使用它来测试实现。请注意,演示项目将 ViewState 保存到 session,我认为这是存储它的最佳位置。
使用代码
正如我所说的,实现非常简单。所以,让我们从我的 Class
代码开始,在讨论完之后,我们再来看 Web.Config 文件的配置。
'KEEPING THE VIEWSTATE OUT OF THE ASPX PAGE
'Autor.: Régis Daniel de Oliveira
'E-Mail: regisxp@hotmail.com
'Date..: 07/2005
'Reserach Source:
'http://www.eggheadcafe.com/articles/20040613.asp
Imports System.Configuration.ConfigurationSettings
Imports System.Diagnostics
Public Class VSPage
Inherits System.Web.UI.Page
现在,让我们来看负责保存 ViewState 数据的 SavePageStateToPersistenceMedium
方法。这个方法所做的就是检查我们 Web.Config 文件中的 ServerSideViewState
是否启用(True
)。如果否,系统将执行正常的 ViewState 保存,将其存储在 ASPX 文档中。如果为是,系统将检查你想使用的 ServerSideViewState
方法:CACHE
或 SESSION
。如果你选择 CACHE
,数据将被存储在缓存中,并在一段时间后过期。如果你选择 SESSION
,数据将被保存在用户会话中,并在用户会话过期时被丢弃。当使用此选项时,系统会自动创建一个 DataTable
,它将被保存在 session 中,并存储所有 ViewState 数据。DataTable
中可以存储的 ViewState 数据的最大数量由 Web.Config 参数 ViewStateTableSize
定义。默认值 150 表示系统中将可用的最后 150 次回发的 ViewState 数据。因此,如果用户点击导航器的“后退”按钮 150 次(这不太可能发生),该页面的 ViewState 将已经可用。这是一个非常好的数字……
Protected Overrides Sub _
SavePageStateToPersistenceMedium(ByVal viewState As Object)
Dim VSKey As String
'String that will hold the Unique Key
'used to reference this ViewState data
Debug.WriteLine(MyBase.Session.SessionID)
'Create the key based on the SessionID, on the Request.RawUrl
'and on the Ticks representated by the exact time
'while the page is being saved
VSKey = "VIEWSTATE_" & MyBase.Session.SessionID & "_" & _
Request.RawUrl & "_" & Date.Now.Ticks.ToString
'Check if the ServerSideViewState is Activated
If UCase(AppSettings("ServerSideViewState")) = "TRUE" Then
'Check were we will save the ViewState Data
If UCase(AppSettings("ViewStateStore")) = "CACHE" Then
'Store the ViewState on Cache
Cache.Add(VSKey, viewState, Nothing, _
Date.Now.AddMinutes(Session.Timeout), _
Cache.NoSlidingExpiration, _
Web.Caching.CacheItemPriority.Default, Nothing)
'The ViewStateData will be Saved on the SESSION
Else
Dim VsDataTable As DataTable
Dim DbRow As DataRow
'Check if the ViewState DataTable are on the Session
If IsNothing(Session("__VSDataTable")) Then
'No, it's not. Create it...
Dim PkColumn(1), DbColumn As DataColumn
VsDataTable = New DataTable("VState")
'Create the DataTable
'Column 1 - Name: VSKey - PrimaryKey
DbColumn = New DataColumn("VSKey", GetType(String))
VsDataTable.Columns.Add(DbColumn)
PkColumn(0) = DbColumn
VsDataTable.PrimaryKey = PkColumn
'Column 2 - Name: ViewStateData
DbColumn = New DataColumn("VSData", GetType(Object))
VsDataTable.Columns.Add(DbColumn)
'Column 3 - Name: DateTime
DbColumn = New DataColumn("DateTime", GetType(Date))
VsDataTable.Columns.Add(DbColumn)
Else
'The ViewState DataTable is already on the UserSession
VsDataTable = Session("__VSDataTable")
End If
'Check if we already have a ViewState saved with the same key.
'If yes, update it instead of creating a new row.
'(This is very dificult to happen)
DbRow = VsDataTable.Rows.Find(VSKey)
If Not IsNothing(DbRow) Then
'Row found!!! Update instead of creating a new one...
DbRow("VsData") = viewState
Else
'Create a new row...
DbRow = VsDataTable.NewRow
DbRow("VSKey") = VSKey
DbRow("VsData") = viewState
DbRow("DateTime") = Date.Now
VsDataTable.Rows.Add(DbRow)
End If
'Check if our DataTable is OverSized...
If Convert.ToInt16(AppSettings("ViewStateTableSize"))_
< VsDataTable.Rows.Count Then
Debug.WriteLine("Deleting ViewState Created On " _
& DbRow(2) & ",ID " & DbRow(0))
VsDataTable.Rows(0).Delete() 'Delete the 1st line.
End If
'Store the DataTable on the Session.
Session("__VSDataTable") = VsDataTable
End If
'Register a HiddenField on the Page,
'that contains ONLY the UniqueKey generated.
'With this, we'll be able to find with ViewState
'is from this page, by retrieving these value.
RegisterHiddenField("__VIEWSTATE_KEY", VSKey)
Else
'Call the normal process.
MyBase.SavePageStateToPersistenceMedium(viewState)
End If
End Sub
现在,让我们来看负责加载 ViewStateData
的 LoadPageStateFromPersistenceMedium
方法。
Protected Overrides Function LoadPageStateFromPersistenceMedium() As Object
'Verifica se o ServerSideViewState está ativado
If UCase(AppSettings("ServerSideViewState")) = "TRUE" Then
Dim VSKey As String 'ViewState UniqueKey
VSKey = Request.Form("__VIEWSTATE_KEY")
'Request the Key from the page and validade it.
If Not VSKey.StartsWith("VIEWSTATE_") Then
Throw New Exception("Invalid VIEWSTATE Key: " & VSKey)
End If
'Verify which <SPAN id=BABID_Results7>modality was used to save ViewState
If UCase(AppSettings("ViewStateStore")) = "CACHE" Then
Return Cache(VSKey)
Else
Dim VsDataTable As DataTable
Dim DbRow As DataRow
VsDataTable = Session("__VSDataTable")
DbRow = VsDataTable.Rows.Find(VSKey)
If IsNothing(DbRow) Then
Throw New Exception("VIEWStateKey not Found. " & _
"Consider increasing the ViewStateTableSize" & _
" parameter on Web.Config file.")
End If
Return DbRow("VsData")
End If
Else
'Return the ViewState using the Norma Method
Return MyBase.LoadPageStateFromPersistenceMedium()
End If
End Function
End Class
现在,这就是我们需要插入到我们配置文件 Web.Config 中的内容。
<!-- application specific settings -->
<appSettings>
<!--ServerSideViewState: Defines if ViewState
will be saved on the Server: True|False-->
<add key="ServerSideViewState" value="True"/>
<!--ViewStateStore: Defines where we'll save the ViewState: Cache|Session-->
<add key="ViewStateStore" value="Session" />
<!--ViewStateCacheFSSize: Define the maximum Number
of viewStates will be saved when ViewStateStore = Session -->
<add key="ViewStateTableSize" value="150" />
</appSettings>
关注点
为了实现本文介绍的登录功能,进行了大量的研究。其中一个最有趣的来源是 这个。如果你想了解更多关于这些技术的信息并查看一些压力测试的结果,请点击链接。
结论
ViewState 确实简化了 ASP.NET 应用程序的开发,但由于页面加载时间的增加,一些开发者不喜欢使用它。通过这项技术,你可以在你的项目中使用 ViewState 的所有优点,而无需担心生成页面源代码的大小。