Click here to Skip to main content
6,634,665 members and growing! (15,508 online)
Email Password   helpLost your password?
Web Development » ASP.NET » General     Intermediate

Manage ViewState using ASP.NET 2.0 Provider Architecture

By Oleg Sobol

ServerSide ViewState management using custom providers based on ASP.NET 2.0 provider pattern architecture.
VB 8.0.NET 2.0, Win2K, WinXP, Win2003, Vista, ASP.NET, WebForms, SQL 2005, VS2005, Architect, DBA, Dev
Posted:8 May 2007
Updated:5 Jun 2007
Views:36,200
Bookmarked:35 times
Unedited contribution
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
4 votes for this article.
Popularity: 2.27 Rating: 3.77 out of 5

1
1 vote, 25.0%
2

3
1 vote, 25.0%
4
2 votes, 50.0%
5

Introduction

This article shows an implementation of a couple of custom viewstate providers using 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 viewstate. And thats when the page load times started taking longer and longer. I started researching alternative solutions for optimizing 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 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 the viewstate management, but none had a complete and easily integrable 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 detailed explanation of pattern design and architecture please see references listed above.

Writing the providers

First, we need an abstract class representing the viewstate and exposing 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 derived classes: LoadPageState and 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

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.

Lets look at one of them - SessionViewStateProvider.

NOTE: This provider requires 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).

    ''' <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 "init">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'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 LoadPageState and SavePageState methods to load/save the viewstate string from/to the database.

Other properties include:
ApplicationName which gets populated from 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 default __VIEWSTATE field, because it gets overwritten by 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 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 viewState from the database.

Web Application Configuration

Viewstate provider requires the following 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 infrastructure for configuration

Since 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 <viewstate> element's defaultProvider attribute, if present, and so on.

    ''' <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

Below is the way our custom section is registered with ASP.NET to be inside 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. ViewStateManager class will be used in actual aspx pages to load/save the viewstate. It will contain a collection of all providers registered in Web.config, with one of them set as default provider.

    ''' <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
</viewstate>

Inside LoadProviders, ProvidersHelper.InstantiateProviders subroutine loops through the viewstate/providers section under system.web in Web.Config and loads 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 the PageTemplate.

PageTemplate has the following exposed besides the overriden viewstate functions:

  • ServerSideViewState - set to false to use regular page viewstate. This property can be set in a Web.config or in page's Init method.
  • SetServerSideViewStateProvider - sets provider to be used on a page. This is a hack I used to enable using different provider on per page basis.
NOTE: Common practice is to use the defaultProvider property in 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 per page basis.

    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 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 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 provider on per page basis using SetServerSideViewStateProvider property will be ignored.

Because Anthem is managing outputting 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 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 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 updated serverSideViewState variable to the response string to be written back to the page
    /// <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("}");
    }
    
  • 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 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);
    }
    
  • 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. ServerSide 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 ViewStateProvidersConfig class.
    - Added enabled property to the Viewstate config section.
    - Added Anthem integration, and a sample page using Anthem datagrid.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Oleg Sobol


Member
Oleg Sobol has been developing web applications in ASP.NET using VB.NET for the past three years. Currently working for a leading online higher education provider, developing new online initiatives and supporting multiple education platforms.
Occupation: Web Developer
Location: United States United States

Other popular ASP.NET articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 7 of 7 (Total in Forum: 7) (Refresh)FirstPrevNext
GeneralAnother approach PinmemberPro Dev10:05 7 Sep '07  
GeneralGreat Job.. Pinmembers_ruchit21:03 5 Jun '07  
GeneralGood stuff but I'm going to play devils advocate Pinmemberbri189b15:40 15 May '07  
AnswerRe: Good stuff but I'm going to play devils advocate PinmemberOleg Sobol18:25 15 May '07  
GeneralRe: Good stuff but I'm going to play devils advocate Pinmembermerlin98112:51 5 Jun '07  
GeneralWow!!! PinmemberRicardo Casquete0:31 15 May '07  
GeneralRe: Wow!!! PinmemberOleg Sobol6:53 15 May '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 5 Jun 2007
Editor:
Copyright 2007 by Oleg Sobol
Everything else Copyright © CodeProject, 1999-2009
Web20 | Advertise on the Code Project