Introduction
This article shows an implementation of a couple of custom viewstate providers using the ASP.NET 2.0 Provider architecture and the Provider Model design pattern. To get more information regarding the ASP.NET 2.0 Provider Model, please read Introduction to the Provider Model.
Background
About half a year ago, I was working on a project that required moderately large datasets fetched from the database and displayed in a DataGrid. Going to the database on every postback seemed like a waste of resources, therefore, I decided to store the data in the viewstate. And, that's when the page load times started taking longer and longer. I started researching alternative solutions for optimizing the viewstate, and found a moderate amount of information and examples online. Nevertheless, I have not been able to find a complete solution that would let me easily manage and extend the functionality of the viewstate without having to make significant changes to the website.
I have looked at a number of interesting and useful articles that provided different takes on the implementation of viewstate management, but none had a complete and easy to integrate solution.
The code presented below was inspired by or partially taken from the following articles:
Using the code
I will concentrate on describing just the viewstate provider code, without going into details on the provider pattern design. For a detailed explanation of the pattern design and architecture, please see the references listed above.
Writing the providers
First, we need an abstract class representing the viewstate and exposing the functions actual providers will implement. ViewStateProvider
is derived from ProviderBase
, and will be the base class for all concrete viewstate provider classes. It will have two functions that must be implemented by the derived classes: LoadPageState
and SavePageState
.
Public MustInherit Class ViewStateProvider
Inherits System.Configuration.Provider.ProviderBase
Public MustOverride Property ApplicationName() As String
Public MustOverride Property ViewStateKeyName() As String
Public MustOverride Function LoadPageState(ByVal pControl As Control) As Object
Public MustOverride Sub SavePageState(ByVal pControl As Control, _
ByVal viewState As Object)
End Class
Next, we need an actual provider class. I have three concrete provider classes in the example:
SessionViewStateProvider
Viewstate is being maintained as a session variable.
CompressionViewStateProvider
Viewstate is being compressed using ICSharpCode.SharpZipLib and written to a page.
SqlViewStateProvider
Viewstate is maintained using a modified ASPState database. The ViewState is written to the database similar to an OutOfProc Session.
Let's look at one of them - SessionViewStateProvider
.
Note: This provider requires a SQL script attached with the code to be executed on the database where the viewstate will be stored. Proper SQL connection and impersonation must be set up in Web.config. All of the SQL script code is taken from Adam Weigert's SqlViewState - The Path to Better ViewState Storage. Other providers in the example do not require any additional setup, except registration in a Web.config (explained here).
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 "init">Initialize(ByVal name As String, _
ByVal config As NameValueCollection)
If config Is Nothing Then Throw New ArgumentNullException("config")
If String.IsNullOrEmpty(name) Then name = "SqlViewStateProvider"
If String.IsNullOrEmpty(config("description")) Then
config.Remove("description")
config.Add("description", "SQL viewstate provider")
End If
MyBase.Initialize(name, config)
_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")
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
's Initialize
method expects to find a configuration attribute named ConnectionStringName
identifying a connection string in the <connectionStrings>
configuration section. The connection string is used by the LoadPageState
and SavePageState
methods to load/save the viewstate string from/to the database.
Other properties include:
ApplicationName
which gets populated from the applicationName attribute.
ViewStateKeyName
property will contain the name of the hidden field where the reference to the viewstate will be kept on the page. This reference cannot be kept in the default __VIEWSTATE
field, because it gets overwritten by the ASP.NET rendering engine.
Timeout
property which sets the time in minutes for how long the database should keep the current viewstate record. A SqlServer job runs every so often and deletes all the expired viewstate records from the table.
SavePageState
serializes the viewState
object passed into it using System.Web.UI.LosFormatter
. Creates a GUID to serve as a unique identifier, and saves the record in a database. The newly generated GUID is then saved in a hidden field variable of a page.
LoadPageState
retrieves the GUID from the hidden variable and gets the viewstate from the database.
The viewstate provider requires the following to be added to the Web.config of the Web Application:
<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>
Explanation of the infrastructure for the configuration
Since the viewstate is not a stock configuration section, a custom configuration section must be written that derives from System.Configuration.ConfigurationSection
. The following is a System.Web.UI.ViewStateSection
class that exposes two properties: Providers
and DefaultProvider
. The <ConfigurationProperty>
attributes map ViewStateSection
properties to <viewstate>
attributes in a configuration file. As a result, the DefaultProvider
property will get its value from the <viewstate>
element's defaultProvider
attribute, if present, and so on.
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
Below is the way our custom section is registered with ASP.NET to be inside the system.web
section of a configuration file. Note: type="System.Web.UI.ViewStateSection, CustomProviders"
has the namespace, class, and assembly name in which the class is located.
<configSections>
<sectionGroup name="system.web">
<section name="viewstate"
type="System.Web.UI.ViewStateSection, CustomProviders"
restartOnExternalChanges="true"
allowDefinition="MachineToApplication" />
</sectionGroup>
</configSections>
Loading and initializing viewstate providers
At last, we need a class that will load and manage all viewstate providers registered in Web.config. The ViewStateManager
class will be used in the actual ASPX pages to load/save the viewstate. It will contain a collection of all the providers registered in the Web.config, with one of them set as the default provider.
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
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
Call LoadProviders()
Return _provider.LoadPageState(pControl)
End Function
Public Shared Sub SavePageState(ByVal pControl As Control, _
ByVal viewState As Object)
Call LoadProviders()
_provider.SavePageState(pControl, viewState)
End Sub
Private Shared Sub LoadProviders()
If _provider Is Nothing Then
SyncLock _lock
If _provider Is Nothing Then
Dim section As ViewStateSection = GetViewStateSection()
If section IsNot Nothing Then _enabled = section.Enabled
_enabledSet = True
If _enabled Then
_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
Inside LoadProviders
, the ProvidersHelper.InstantiateProviders
subroutine loops through the viewstate/providers section under system.web
in Web.Config and loads the registered providers. It looks at the following to load a provider:
<add name="SqlViewStateProvider"
type="System.Web.Configuration.Providers.SqlViewStateProvider,
CustomViewStateProviders"
connectionStringName="ViewStateConnectionString" timeout="35" />
Using ViewStateManager
Now we need to overload the functions exposed by the Page
class to use our viewstate manager to save/load the viewstate. In the example, this is done by deriving a PageTemplate
class that inherits from System.Web.UI.Page
, and making all ASPX pages inherit from PageTemplate
.
PageTemplate
has the following exposed besides the overridden viewstate functions:
ServerSideViewState
- set to false
to use a regular page viewstate. This property can be set in Web.config or in the page's Init
method.
SetServerSideViewStateProvider
- sets the provider to be used on a page. This is a hack I used to enable using different providers on a per page basis.
Note: A common practice is to use the defaultProvider
property in the Web.config to set the provider for the whole application. Using SetServerSideViewStateProvider
could get messy fast, and is not a best practice in my opinion.
Below is the proper code for the overloaded page. The code in the example will differ slightly to show different implementations on a per page basis.
Protected Overloads Overrides Function LoadPageStateFromPersistenceMedium() As Object
If _serverSideViewState AndAlso ViewStateManager.Enabled Then
Return ViewStateManager.LoadPageState(Me)
Else
Return MyBase.LoadPageStateFromPersistenceMedium
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)
End If
End Sub
Anthem.Net integration
Anthem.Net is, in my opinion, the best AJAX library out to date. And, it would be a shame if it couldn't be used in conjunction with the CustomViewState implementation. To read more on Anthem.Net, please see: Introduction to Anthem.NET.
Note: This solution will always use the default viewstate provider set in web.config. Setting the provider on a per page basis using the SetServerSideViewStateProvider
property will be ignored.
Because Anthem is managing outputting the ViewState to the page separately from ASP.NET, we need to make Anthem write our hidden variable with the ViewStateKeyName
to the page if it exists. Retrieval and loading of viewstate will be handled by ASP.NET as the normal page lifecycle is started by Anthem during a callback.
To integrate ViewStateManager, we need to make the following modifications in the Anthem project:
- Add CustomProviders.dll as a reference to the Anthem project in order to get access to ViewStateManager.
- Add the following function in Manager.cs to retrieve the viewstate key from the page's markup after the callback.
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;
}
}
Create an overloaded WriteValueAndError
function in Manager.cs, which will add values including an updated serverSideViewState
variable to the response string to be written back to the page.
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("}");
}
Next, we need to modify the WriteResult(Stream stream, MemoryStream htmlBuffer)
function in Manager.cs to integrate CustomViewState. This function sends the result string back to the page. Modifications are shown in bold:
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 (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)
{
sb.Length = 0;
WriteValueAndError(sb, null, ex.Message, null, null, null, null, null);
}
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);
}
Finally, we need to modify Anthem.js to update the serverSideViewState
hidden input value on a page. Modifications are shown in bold:
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);
}
}
That's it. The server-side viewstate makes Anthem noticeably more responsive. Check it out for yourself!
Points of interest
This code has been written but, unfortunately, never been tested in a real life application. Very interested to see SqlViewStateProvider
and other providers get used in a large scale application with a high usage. Any ideas or other provider (better) implementations? All feedback is most welcome.
History
- June 5, 2007
- Removed the
ViewStateProvidersConfig
class.
- Added the
Enabled
property to the Viewstate config section.
- Added Anthem integration, and a sample page using the Anthem datagrid.