使用 ASP.NET 2.0 提供程序体系结构管理 ViewState






3.93/5 (5投票s)
使用基于 ASP.NET 2.0 提供程序模式体系结构的自定义提供程序进行服务器端 ViewState 管理。
引言
本文展示了使用 ASP.NET 2.0 提供程序体系结构和提供程序模型设计模式实现的几个自定义 viewstate 提供程序。有关 ASP.NET 2.0 提供程序模型的更多信息,请阅读提供程序模型简介。
背景
大约半年前,我正在处理一个项目,该项目需要从数据库中提取中等大小的数据集并在 DataGrid 中显示。每次回发都去数据库似乎是在浪费资源,因此,我决定将数据存储在 viewstate 中。就这样,页面加载时间开始变得越来越长。我开始研究优化 viewstate 的替代解决方案,并在网上找到了一些信息和示例。尽管如此,我还没有找到一个完整的解决方案,可以让我轻松地管理和扩展 viewstate 的功能,而无需对网站进行重大更改。
我查阅了许多有趣且有用的文章,这些文章提供了对 viewstate 管理实现的不同看法,但没有一篇是完整且易于集成的解决方案。
下面的代码受到以下文章的启发或部分摘录自
- 自定义基于提供程序的Services - ASP.NET 2.0 提供程序体系结构和模式设计。
- ViewState Provider - an implementation using the Provider Model Design Pattern - ASP.NET 1.1 实现
- Analysis of Keeping ViewState out of the Page - SessionViewState 提供程序理念、性能统计和有益的讨论
- Keep ASP.NET ViewState out of ASPX Page for Performance Improvement - SessionViewState 提供程序理念
- SqlViewState - 更好的 ViewState 存储之路
使用代码
我将专注于描述 viewstate 提供程序代码,而不深入介绍提供程序模式设计。有关模式设计和体系结构的详细解释,请参阅上面列出的参考文献。
编写提供程序
首先,我们需要一个抽象类来表示 viewstate 并公开实际提供程序将实现的函数。ViewStateProvider
继承自 ProviderBase
,并将成为所有具体 viewstate 提供程序类的基类。它将有两个必须由派生类实现的函数:LoadPageState
和 SavePageState
。
''' <summary>
''' Defines the contract that ASP.NET implements to provide viewstate
''' services using custom viewstate providers.
''' </summary>
Public MustInherit Class ViewStateProvider
Inherits System.Configuration.Provider.ProviderBase
Public MustOverride Property ApplicationName() As String
''' <summary>
''' The hidden field variable name where
''' our viewstate reference key is stored on a page.
''' </summary>
Public MustOverride Property ViewStateKeyName() As String
''' -----------------------------------------------------------------------------
''' <summary>
''' Loads any saved view-state of the current page from virtually any
''' storage medium other than a hidden field
''' </summary>
''' <param name="pControl">System.Web.UI.Page</param>
''' <returns>The saved view state</returns>
''' -----------------------------------------------------------------------------
Public MustOverride Function LoadPageState(ByVal pControl As Control) As Object
''' -----------------------------------------------------------------------------
''' <summary>
''' Saves any view-state information of the page to virtually any
''' storage medium other than a hidden field
''' </summary>
''' <param name="pControl">System.Web.UI.Page</param>
''' <param name="viewState">A System.Object
''' in which to store the view-state information</param>
''' -----------------------------------------------------------------------------
Public MustOverride Sub SavePageState(ByVal pControl As Control, _
ByVal viewState As Object)
End Class
接下来,我们需要一个实际的提供程序类。在示例中,我有三个具体的提供程序类
SessionViewStateProvider
CompressionViewStateProvider
SqlViewStateProvider
ViewState 被维护为一个会话变量。
ViewState 使用 ICSharpCode.SharpZipLib 进行压缩并写入页面。
ViewState 使用修改后的 ASPState 数据库进行维护。ViewState 被写入数据库,类似于 OutOfProc Session。
让我们看看其中一个 - SessionViewStateProvider
。
注意:此提供程序需要与代码一起附加的 SQL 脚本,并在将存储 viewstate 的数据库上执行。必须在 Web.config 中设置正确的 SQL 连接和模拟。所有 SQL 脚本代码均摘自 Adam Weigert 的SqlViewState - 更好的 ViewState 存储之路。示例中的其他提供程序不需要任何额外的设置,除了在 Web.config 中注册(在此处进行了解释)。
''' <summary>
''' Manages storage of viewstate object for an ASP.NET
''' application in a SQL Server database.
''' </summary>
''' <remarks><para><pre>
''' RevisionHistory:
''' --------------------------------------------------------------------------------
''' Date Name Description
''' --------------------------------------------------------------------------------
''' 10/11/2006 Oleg Sobol Initial Creation
''' inspiration taken from
''' http://weblogs.asp.net/adweigert/archive/2004/03/09/
''' sqlviewstate-the-path-to-better-viewstate-storage.aspx
''' 5/11/2007 Oleg Sobol Added ViewStateKeyName and DEFAULT_TIMEOUT
''' </pre></para></remarks>
Public Class SqlViewStateProvider
Inherits System.Web.UI.ViewStateProvider
Public Const DEFAULT_TIMEOUT As Integer = 25
Private _applicationName As String
Private _connectionString As String
Private _connectionStringName As String
Private _timeout As TimeSpan
Private _enableViewStateMac As Boolean
Private _lockLoad As New Object
Private _lockSave As New Object
Private _viewStateKeyName As String = "__VIEWSTATE_KEY"
Public Overloads Overrides Property ApplicationName() As String
Get
Return _applicationName
End Get
Set(ByVal value As String)
_applicationName = value
End Set
End Property
Public Property ConnectionStringName() As String
Get
Return _connectionStringName
End Get
Set(ByVal value As String)
_connectionStringName = value
End Set
End Property
Public Property Timeout() As TimeSpan
Get
Return _timeout
End Get
Set(ByVal value As TimeSpan)
_timeout = value
End Set
End Property
Public Property EnableViewStateMac() As Boolean
Get
Return _enableViewStateMac
End Get
Set(ByVal value As Boolean)
_enableViewStateMac = value
End Set
End Property
Public Overrides Property ViewStateKeyName() As String
Get
Return _viewStateKeyName
End Get
Set(ByVal value As String)
_viewStateKeyName = value
End Set
End Property
Public Overloads Overrides Sub Initialize(ByVal name As String, _
ByVal config As NameValueCollection)
' Verify that config isn't null
If config Is Nothing Then Throw New ArgumentNullException("config")
' Assign the provider a default name if it doesn't have one
If String.IsNullOrEmpty(name) Then name = "SqlViewStateProvider"
' Add a default "description" attribute
' to config if the attribute doesn't exist or is empty
If String.IsNullOrEmpty(config("description")) Then
config.Remove("description")
config.Add("description", "SQL viewstate provider")
End If
' Call the base class's Initialize method
MyBase.Initialize(name, config)
' Initialize _applicationName
_applicationName = config("applicationName")
If String.IsNullOrEmpty(_applicationName) Then _applicationName = "/"
config.Remove("applicationName")
Dim connect As String = config("connectionStringName")
If String.IsNullOrEmpty(connect) Then
Throw New ViewStateProviderException(_
"Empty or missing connectionStringName")
End If
config.Remove("connectionStringName")
If WebConfigurationManager.ConnectionStrings(connect) Is Nothing Then
Throw New ViewStateProviderException("Missing connection string")
End If
_connectionString = _
WebConfigurationManager.ConnectionStrings(connect).ConnectionString
If String.IsNullOrEmpty(_connectionString) Then
Throw New ViewStateProviderException("Empty connection string")
End If
Dim timeout As String = config("timeout")
If String.IsNullOrEmpty(timeout) OrElse Not IsNumeric(timeout) Then
_timeout = TimeSpan.FromMinutes(_
ViewStateProvidersConfig.DefaultViewStateTimeout)
Else
_timeout = TimeSpan.FromMinutes(CInt(timeout))
End If
config.Remove("timeout")
Dim enableViewStateMac As String = config("enableViewStateMac")
Try
_enableViewStateMac = CBool(enableViewStateMac)
Catch ex As Exception
_enableViewStateMac = False
End Try
config.Remove("enableViewStateMac")
' Throw an exception if unrecognized attributes remain
If config.Count > 0 Then
Dim attr As String = config.GetKey(0)
If Not String.IsNullOrEmpty(attr) Then _
Throw New ViewStateProviderException(_
"Unrecognized attribute: " + attr)
End If
End Sub
Public Overrides Function LoadPageState(ByVal pControl As _
System.Web.UI.Control) As Object
Dim connection As SqlConnection = Nothing
Dim rawData As Byte() = Nothing
Dim stream As MemoryStream = Nothing
Try
Dim viewStateGuid As Guid = GetViewStateGuid(pControl)
connection = New SqlConnection(_connectionString)
Dim command As SqlCommand = _
New SqlCommand("GetViewState", connection)
Try
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add("@returnValue", _
SqlDbType.Int).Direction = ParameterDirection.ReturnValue
command.Parameters.Add("@viewStateId", _
SqlDbType.UniqueIdentifier).Value = viewStateGuid
connection.Open()
Dim reader As SqlDataReader = command.ExecuteReader
Try
If reader.Read Then
rawData = CType(Array.CreateInstance(GetType(Byte), _
reader.GetInt32(0)), Byte())
End If
If reader.NextResult AndAlso reader.Read Then
reader.GetBytes(0, 0, rawData, 0, rawData.Length)
End If
Catch e As Exception
Throw New ViewStateProviderException(_
"Problem reading data returned from SqlServer", e)
Finally
CType(reader, IDisposable).Dispose()
End Try
Catch e As Exception
Throw New ViewStateProviderException("Problem executing SqlCommand", e)
Finally
If Not command Is Nothing Then command.Dispose()
End Try
Catch e As Exception
Throw New ViewStateProviderException("Problem with SqlConnection", e)
Finally
If Not connection Is Nothing Then connection.Dispose()
End Try
Try
stream = New MemoryStream(rawData)
Return Me.GetLosFormatter(pControl, False).Deserialize(stream)
Catch e As Exception
Throw New ViewStateProviderException("Problem with data deserialization", e)
Finally
If Not stream Is Nothing Then CType(stream, IDisposable).Dispose()
End Try
Return Nothing
End Function
Public Overrides Sub SavePageState(ByVal pControl As _
System.Web.UI.Control, ByVal viewState As Object)
Dim p As Page = Nothing
Dim viewStateGuid As Guid
Dim stream As MemoryStream = Nothing
Try
p = CType(pControl, Page)
viewStateGuid = GetViewStateGuid(p)
stream = New MemoryStream
GetLosFormatter(p, _enableViewStateMac).Serialize(stream, viewState)
Dim connection As SqlConnection = New SqlConnection(_connectionString)
Try
Dim command As SqlCommand = New SqlCommand("SetViewState", connection)
Try
command.CommandType = CommandType.StoredProcedure
command.Parameters.Add("@returnValue", _
SqlDbType.Int).Direction = ParameterDirection.ReturnValue
command.Parameters.Add("@viewStateId", _
SqlDbType.UniqueIdentifier).Value = viewStateGuid
command.Parameters.Add("@value", _
SqlDbType.Image).Value = stream.ToArray
command.Parameters.Add("@timeout", _
SqlDbType.Int).Value = _timeout.TotalMinutes
connection.Open()
command.ExecuteNonQuery()
Catch e As Exception
System.Diagnostics.Trace.Write(e.Message)
Finally
If Not command Is Nothing Then command.Dispose()
End Try
Catch e As Exception
System.Diagnostics.Trace.Write(e.Message)
Finally
If Not connection Is Nothing Then connection.Dispose()
End Try
Catch e As Exception
System.Diagnostics.Trace.Write(e.Message)
Finally
If Not stream Is Nothing Then CType(stream, IDisposable).Dispose()
End Try
Dim control As Html.HtmlInputHidden _
= CType(p.FindControl(ViewStateProvidersConfig.ViewStateKeyFieldName), _
Html.HtmlInputHidden)
If control Is Nothing Then
p.ClientScript.RegisterHiddenField(_
ViewStateProvidersConfig.ViewStateKeyFieldName, _
viewStateGuid.ToString)
Else
control.Value = viewStateGuid.ToString
End If
End Sub
#Region " Private Helper Functions "
Private Function GetViewStateGuid(ByVal pControl As Control) As Guid
Dim p As Page = CType(pControl, Page)
Dim viewStateKey As String = _
p.Request.Form(ViewStateProvidersConfig.ViewStateKeyFieldName)
If viewStateKey Is Nothing OrElse viewStateKey.Length < 1 Then
viewStateKey = _
p.Request.QueryString(ViewStateProvidersConfig.ViewStateKeyFieldName)
If viewStateKey Is Nothing OrElse viewStateKey.Length < 1 Then
Return Guid.NewGuid
End If
End If
Try
Return New Guid(viewStateKey)
Catch e As FormatException
Return Guid.NewGuid
End Try
End Function
Private Function GetMacKeyModifier(ByVal pControl As Control) As String
Dim p As Page = CType(pControl, Page)
Dim value As Integer = p.TemplateSourceDirectory.GetHashCode + _
Me.GetType.Name.GetHashCode
If Not (p.ViewStateUserKey Is Nothing) Then
Return String.Concat(value.ToString(_
NumberFormatInfo.InvariantInfo), p.ViewStateUserKey)
End If
Return value.ToString(NumberFormatInfo.InvariantInfo)
End Function
Private Function GetLosFormatter(ByVal pControl As Control, _
ByVal enableViewStateMac As Boolean) _
As LosFormatter
If enableViewStateMac Then
Return New LosFormatter(True, _
GetMacKeyModifier(CType(pControl, Page)))
Return New LosFormatter
End Function
#End Region
End Class
SessionViewStateProvider
的 Initialize
方法期望找到一个名为 ConnectionStringName
的配置属性,该属性标识 <connectionStrings>
配置部分中的连接字符串。连接字符串由 LoadPageState
和 SavePageState
方法使用,以从数据库加载/保存 viewstate 字符串。
其他属性包括
ApplicationName
,它从 applicationName 属性中获取。ViewStateKeyName
属性将包含 viewstate 引用在页面上保留的隐藏字段的名称。此引用不能保留在默认的__VIEWSTATE
字段中,因为它会被 ASP.NET 渲染引擎覆盖。Timeout
属性,用于设置数据库保留当前 viewstate 记录的时间(以分钟为单位)。SqlServer 作业会定期运行并删除表中所有过期的 viewstate 记录。SavePageState
使用System.Web.UI.LosFormatter
序列化传递给它的viewState
对象。创建一个 GUID 作为唯一标识符,并将记录保存在数据库中。新生成的 GUID 然后保存在页面的隐藏字段变量中。LoadPageState
从隐藏变量中检索 GUID,并从数据库获取 viewstate。
Web 应用程序配置
viewstate 提供程序需要在 Web 应用程序的 Web.config 中添加以下内容
<configuration>
<configSections>
<sectionGroup name="system.web">
<section name="viewstate"
type="System.Web.UI.ViewStateSection, CustomProviders"
restartOnExternalChanges="true"
allowDefinition="MachineToApplication" />
</sectionGroup>
</configSections>
<connectionStrings>
<add name="ViewStateConnectionString"
connectionString="Server=(local);Database=ASPState;
Integrated Security=True;" />
</connectionStrings>
<system.web>
<viewstate defaultProvider="CompressionViewStateProvider"
enabled="true">
<providers>
<add name="SqlViewStateProvider"
type="System.Web.Configuration.Providers.SqlViewStateProvider,
CustomViewStateProviders"
connectionStringName="ViewStateConnectionString"
timeout="35" />
<add name="SessionViewStateProvider"
type="System.Web.Configuration.Providers.SessionViewStateProvider,
CustomViewStateProviders"
numberOfPagesInMemory="10" />
<add name="CompressionViewStateProvider"
type="System.Web.Configuration.Providers.CompressionViewStateProvider,
CustomViewStateProviders" />
</providers>
</viewstate>
<system.web>
</configuration>
配置基础结构的解释
由于 viewstate 不是股票配置节,因此必须编写一个自定义配置节,该节继承自 System.Configuration.ConfigurationSection
。以下是 System.Web.UI.ViewStateSection
类,它公开两个属性:Providers
和 DefaultProvider
。<ConfigurationProperty>
属性将 ViewStateSection
属性映射到配置文件中的 <viewstate>
属性。因此,如果存在,DefaultProvider
属性将从 <viewstate>
元素的 defaultProvider
属性获取其值,依此类推。
''' <summary>
''' Maps to a <viewstate> section in a configuration file
''' </summary>
Public Class ViewStateSection
Inherits ConfigurationSection
<ConfigurationProperty("providers")> _
Public ReadOnly Property Providers() As ProviderSettingsCollection
Get
Return CType(MyBase.Item("providers"), ProviderSettingsCollection)
End Get
End Property
<ConfigurationProperty("defaultProvider"), DefaultSettingValue("")> _
Public Property DefaultProvider() As String
Get
Return CStr(MyBase.Item("defaultProvider"))
End Get
Set(ByVal value As String)
MyBase.Item("defaultProvider") = value
End Set
End Property
End Class
以下是我们的自定义节在 ASP.NET 中注册的方式,以便位于配置文件的 system.web
节内。注意:type="System.Web.UI.ViewStateSection, CustomProviders"
包含类所在的命名空间、类和程序集名称。
<configSections>
<sectionGroup name="system.web">
<section name="viewstate"
type="System.Web.UI.ViewStateSection, CustomProviders"
restartOnExternalChanges="true"
allowDefinition="MachineToApplication" />
</sectionGroup>
</configSections>
加载和初始化 viewstate 提供程序
最后,我们需要一个类来加载和管理在 Web.config 中注册的所有 viewstate 提供程序。ViewStateManager
类将在实际的 ASPX 页面中用于加载/保存 viewstate。它将包含在 Web.config 中注册的所有提供程序的集合,其中一个被设置为默认提供程序。
''' <summary>
''' Manages all viewstate manipulations.
''' </summary>
Public Class ViewStateManager
Private Shared _provider As ViewStateProvider = Nothing
Private Shared _providers As ViewStateProviderCollection = Nothing
Private Shared _lock As New Object
Private Shared _enabled As Boolean
Private Shared _enabledSet As Boolean
''' <summary>
''' Default provider set in web.config
''' </summary>
Public Shared ReadOnly Property Provider() As ViewStateProvider
Get
Return _provider
End Get
End Property
Public Shared ReadOnly Property Providers() As ViewStateProviderCollection
Get
Return _providers
End Get
End Property
Public Shared ReadOnly Property Enabled() As Boolean
Get
If Not _enabledSet Then
_enabled = GetViewStateSection().Enabled
_enabledSet = True
End If
Return _enabled
End Get
End Property
Shared Sub New()
Call LoadProviders()
End Sub
Public Shared Function LoadPageState(ByVal pControl As Control) As Object
' Make sure a provider is loaded
Call LoadProviders()
' Delegate to the provider
Return _provider.LoadPageState(pControl)
End Function
Public Shared Sub SavePageState(ByVal pControl As Control, _
ByVal viewState As Object)
' Make sure a provider is loaded
Call LoadProviders()
' Delegate to the provider
_provider.SavePageState(pControl, viewState)
End Sub
Private Shared Sub LoadProviders()
' Avoid claiming lock if providers are already loaded
If _provider Is Nothing Then
SyncLock _lock
' Do this again to make sure _provider is still null
If _provider Is Nothing Then
' Get a reference to the <viewstate> section
Dim section As ViewStateSection = GetViewStateSection()
' Is custom viewstate management enabled
If section IsNot Nothing Then _enabled = section.Enabled
_enabledSet = True
If _enabled Then
' Load registered providers and
' point _provider to the default provider
_providers = New ViewStateProviderCollection
ProvidersHelper.InstantiateProviders(section.Providers, _
_providers, GetType(ViewStateProvider))
_provider = _providers(section.DefaultProvider)
If _provider Is Nothing Then
Throw New ViewStateProviderException(_
"Unable to load default ViewStateProvider")
End If
End If
End If
End SyncLock
End If
End Sub
Private Shared Function GetViewStateSection() As ViewStateSection
Return CType(WebConfigurationManager.GetSection(_
"system.web/viewstate"), ViewStateSection)
End Function
End Class
在 LoadProviders
内部,ProvidersHelper.InstantiateProviders
子例程循环遍历 Web.Config 中 system.web
下的 viewstate/providers 部分并加载注册的提供程序。它会查看以下内容来加载提供程序
<add name="SqlViewStateProvider"
type="System.Web.Configuration.Providers.SqlViewStateProvider,
CustomViewStateProviders"
connectionStringName="ViewStateConnectionString" timeout="35" />
使用 ViewStateManager
现在我们需要重载 Page
类公开的函数,以使用我们的 viewstate管理器来保存/加载 viewstate。在示例中,这是通过派生一个继承自 System.Web.UI.Page
的 PageTemplate
类来实现的,并使所有 ASPX 页面继承自 PageTemplate
。
PageTemplate
除了重载的 viewstate 函数外,还公开以下内容
ServerSideViewState
- 设置为false
以使用常规页面 viewstate。此属性可以在 Web.config 中或在页面的Init
方法中设置。SetServerSideViewStateProvider
- 设置要在页面上使用的提供程序。这是我用来启用按页面使用不同提供程序的技巧。
注意:常见的做法是使用 Web.config 中的 defaultProvider
属性为整个应用程序设置提供程序。我个人认为,使用 SetServerSideViewStateProvider
可能会很快变得混乱,并且不是最佳实践。
以下是重载页面的正确代码。示例中的代码会略有不同,以在按页面基础上显示不同的实现。
Protected Overloads Overrides Function LoadPageStateFromPersistenceMedium() As Object
If _serverSideViewState AndAlso ViewStateManager.Enabled Then
Return ViewStateManager.LoadPageState(Me)
Else
Return MyBase.LoadPageStateFromPersistenceMedium
' regular client viewState
End If
End Function
Protected Overloads Overrides Sub SavePageStateToPersistenceMedium(ByVal viewState As Object)
If _serverSideViewState AndAlso ViewStateManager.Enabled Then
ViewStateManager.SavePageState(Me, viewState)
Else
MyBase.SavePageStateToPersistenceMedium(viewState)
' regular client viewState
End If
End Sub
Anthem.Net 集成
在我看来,Anthem.Net 是迄今为止最好的 AJAX 库。如果它不能与 CustomViewState 实现结合使用,那就太可惜了。要了解更多关于 Anthem.Net 的信息,请参阅:Anthem.NET 简介。
注意:此解决方案将始终使用 web.config 中设置的默认 viewstate 提供程序。使用 SetServerSideViewStateProvider
属性按页面设置提供程序将被忽略。
由于 Anthem 会独立于 ASP.NET 管理 ViewState 到页面的输出,因此我们需要让 Anthem 在页面上写入带有 ViewStateKeyName
的隐藏字段(如果存在)。ViewState 的检索和加载将由 ASP.NET 处理,因为 Anthem 在回调期间会启动正常的页面生命周期。
要集成 ViewStateManager,我们需要在 Anthem 项目中进行以下修改
- 将 CustomProviders.dll 添加为 Anthem 项目的引用,以便访问 ViewStateManager。
- 在 Manager.cs 中添加以下函数,以在回调后从页面的标记中检索 viewstate 键。
private string GetServerSideViewState(string html)
{
if (ViewStateManager.Enabled) {
return GetHiddenInputValue(html, "<input type=\"hidden\" name=\"" +
ViewStateManager.Provider.ViewStateKeyName + "\" id=\"" +
ViewStateManager.Provider.ViewStateKeyName + "\" value=\"");
}
else {
return null;
}
}
WriteValueAndError
函数,该函数会将值(包括更新的 serverSideViewState
变量)添加到要写回页面的响应字符串中。/// <summary>
/// Adds ServerSideViewState support
/// </summary>
private static void WriteValueAndError(
StringBuilder sb,
object val,
string error,
string viewState,
string viewStateEncrypted,
string serverSideViewState,
string serverSideViewStateKey,
string eventValidation,
Hashtable controls,
string[] scripts)
{
sb.Append("{\"value\":");
WriteValue(sb, val);
sb.Append(",\"error\":");
WriteValue(sb, error);
if (viewState != null)
{
sb.Append(",\"viewState\":");
WriteValue(sb, viewState);
}
if (viewStateEncrypted != null)
{
sb.Append(",\"viewStateEncrypted\":");
WriteValue(sb, viewStateEncrypted);
}
if (serverSideViewState != null)
{
sb.Append(",\"serverSideViewState\":");
WriteValue(sb, serverSideViewState);
}
if (serverSideViewStateKey != null)
{
sb.Append(",\"serverSideViewStateKey\":");
WriteValue(sb, serverSideViewStateKey);
}
if (eventValidation != null)
{
sb.Append(",\"eventValidation\":");
WriteValue(sb, eventValidation);
}
if (controls != null && controls.Count > 0)
{
sb.Append(",\"controls\":{");
foreach (DictionaryEntry control in controls)
{
sb.Append("\"" + control.Key + "\":");
WriteValue(sb, control.Value);
sb.Append(",");
}
--sb.Length;
sb.Append("}");
}
if (scripts != null && scripts.Length > 0)
{
sb.Append(",\"pagescript\":[");
foreach (string script in scripts)
{
WriteValue(sb, script);
sb.Append(",");
}
--sb.Length;
sb.Append("]");
}
if (GetManager()._clientSideEvalScripts.Count > 0)
{
sb.Append(",\"script\":[");
foreach (string script in GetManager()._clientSideEvalScripts)
{
WriteValue(sb, script);
sb.Append(",");
}
--sb.Length;
sb.Append("]");
}
sb.Append("}");
}
WriteResult(Stream stream, MemoryStream htmlBuffer)
函数以集成 CustomViewState。此函数将结果字符串发送回页面。修改处以**粗体**显示internal void WriteResult(Stream stream, MemoryStream htmlBuffer)
{
string viewState = null;
string serverSideViewState = null;
string viewStateEncrypted = null;
string eventValidation = null;
Hashtable controls = null;
string[] scripts = null;
if (_updatePage)
{
string html =
HttpContext.Current.Response.ContentEncoding.GetString(
htmlBuffer.GetBuffer());
viewState = GetViewState(html);
serverSideViewState = GetServerSideViewState(html);
#if V2
viewStateEncrypted = GetViewStateEncrypted(html);
eventValidation = GetEventValidation(html);
#endif
controls = GetControls(html);
foreach (object o in _targets.Values)
{
Control c = o as Control;
if (c != null && !c.Visible)
{
if (c.ID != null && controls.ContainsKey(c.ID))
controls[c.ID] = "";
}
}
scripts = GetScripts(html);
}
StringBuilder sb = new StringBuilder();
try
{
// If the serverSideViewState is null,
// that means CustomViewState is turned off on the page,
// and thus will be ignored inside WriteValueAndError.
// But we will double check for it here anyways.
if (ViewStateManager.Enabled && serverSideViewState != null) {
WriteValueAndError(sb, _value, _error, viewState, viewStateEncrypted,
serverSideViewState, ViewStateManager.Provider.ViewStateKeyName,
eventValidation, controls, scripts);
}
else {
WriteValueAndError(sb, _value, _error, viewState, viewStateEncrypted,
eventValidation, controls, scripts);
}
}
catch (Exception ex)
{
// If an exception was thrown while formatting the
// result value, we need to discard whatever was
// written and start over with nothing but the error
// message.
sb.Length = 0;
WriteValueAndError(sb, null, ex.Message, null, null, null, null, null);
}
// If an IOFrame was used to make this callback,
// then wrap the response in a <textarea> element
// so the iframe will not mess with the text of the JSON object.
string response = sb.ToString();
if (string.Compare(HttpContext.Current.Request["Anthem_IOFrame"], "true", true) == 0)
{
response = "<textarea id=\"response\">" +
response + "</textarea>";
}
byte[] buffer = HttpContext.Current.Response.ContentEncoding.GetBytes(response);
stream.Write(buffer, 0, buffer.Length);
}
serverSideViewState
隐藏输入值。修改处以**粗体**显示function Anthem_UpdatePage(result) {
var form = Anthem_GetForm();
if (result.viewState) {
Anthem_SetHiddenInputValue(form, "__VIEWSTATE", result.viewState);
}
if (result.serverSideViewState && result.serverSideViewStateKey) {
Anthem_SetHiddenInputValue(form, result.serverSideViewStateKey,
result.serverSideViewState);
}
if (result.viewStateEncrypted) {
Anthem_SetHiddenInputValue(form, "__VIEWSTATEENCRYPTED",
result.viewStateEncrypted);
}
if (result.eventValidation) {
Anthem_SetHiddenInputValue(form, "__EVENTVALIDATION",
result.eventValidation);
}
if (result.controls) {
for (var controlID in result.controls) {
var containerID = "Anthem_" +
controlID.split("$").join("_") + "__";
var control = document.getElementById(containerID);
if (control) {
control.innerHTML = result.controls[controlID];
if (result.controls[controlID] == "") {
control.style.display = "none";
} else {
control.style.display = "";
}
}
}
}
if (result.pagescript) {
Anthem_LoadPageScript(result, 0);
}
}
就这样。服务器端 viewstate 使 Anthem 响应明显更快。您自己可以亲自试试!
关注点
此代码已编写,但遗憾的是,从未在实际应用程序中进行过测试。非常希望看到 SqlViewStateProvider
和其他提供程序在大规模、高使用量的应用程序中使用。有什么想法或其他的(更好的)提供程序实现吗?非常欢迎所有反馈。
历史
- 2007 年 6 月 5 日
- 删除了
ViewStateProvidersConfig
类。 - 向 Viewstate 配置节添加了
Enabled
属性。 - 添加了 Anthem 集成,以及一个使用 Anthem 数据网格的示例页面。