This is a migrated thread and some comments may be shown as answers.

Persisting ViewState and ControlState to DB/Disk breaks functionality

11 Answers 177 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Massimiliano
Top achievements
Rank 1
Massimiliano asked on 08 Jul 2013, 02:02 PM
It seems that persisting the states on other media and not in the default hiddenfield breaks RadGrid funcionalities (for example paging with "next page" stops at page "2").
The code for changing persistance medium of the viewstate/controlstatef is straightforward and it's derived from Microsoft suggestions on this procedeure.        
We also debugged it and both the viewstate and controlstate are correctly saved and restored from both DB or disk.
A simple test on the viewstate was 

If Not Page.IsPostBack Then
            ViewState("testme") = "test view state"
        Else
            TestLabel.Text = "--" & ViewState("testme")
        End If

And about controlstate we readi it on the debugger and this is a screenshot (both in the first page load and after postback same identical controlstate)

http://www.hakkar.it/controlstate-nopostback.jpg

Implementation is in a base page class derived from all pages:
Private pPageStatePersister As PageStatePersister
 
' DB/Disk persisted viewstate
Protected Overrides ReadOnly Property PageStatePersister As PageStatePersister
    Get
        If pPageStatePersister Is Nothing Then
            If Not String.IsNullOrEmpty(GlobalAppSettings.ViewStatePersister) Then
                pPageStatePersister = New ViewStatePersister(Me)
            Else
                pPageStatePersister = New HiddenFieldPageStatePersister(Me)
            End If
        End If
 
        Return pPageStatePersister
    End Get
End Property

Any hint on why this implementation could break grid functionality while removing it everything works as expected?
Does RadGrid check the "__Viewstate" hidden field directly instead of accessing controlstate/viewstate?


11 Answers, 1 is accepted

Sort by
0
Tsvetoslav
Telerik team
answered on 11 Jul 2013, 02:31 PM
Hello Massimiliano,

Certainly no - the ViewState persisting mechanism is transparent to RadGrid. I suspect the cause for the issue lies somewhere in the persister itself.
 
Regards,
Tsvetoslav
Telerik
If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to the blog feed now.
0
Massimiliano
Top achievements
Rank 1
answered on 11 Jul 2013, 07:33 PM
Hallo Tsvetoslav and thank you for joining the discussion
Of course this was my first thought as well but couldn't find wehre the persister could be the root of this problem.
As you can see in the code above I made little test to check if viewstate is saved and recovered successfully with

If Not Page.IsPostBack Then
      ViewState("testme") = "test view state"
Else
      TestLabel.Text = "--" & ViewState("testme")
End If

And it works flawslessy. Infact when I push "next page" on my ajaxified RadGrid, the label shows up (I put both the grid and the label in the radajaxmanager update triggered by the grid) with the value "--testme" as it should.
I don't know how to do a similiar check for ControlState if is there any way to (I attached above screenshots on first page load with no postback and after pagination).

The code in the persister is quite simple on it's own:

Namespace Eva.Settings
    Public Class ViewStatePersister
        Inherits HiddenFieldPageStatePersister
 
        Private ReadOnly connectionString As String
        Private ReadOnly formatter As LosFormatter
 
        Public Sub New(page As Page)
            MyBase.New(page)
            connectionString = GlobalAppSettings.ConnectionString
            formatter = New LosFormatter()
        End Sub
 
        Public Overrides Sub Load()
            ' Get view state from the hidden field in web page
            Dim viewStateId As String = Page.Request.Form("__EVAVS")
 
            If Not String.IsNullOrWhiteSpace(viewStateId) Then
                If GlobalAppSettings.ViewStatePersister = "SQL" Then
                    LoadFromDb(viewStateId)
                Else
                    LoadFromDisk(viewStateId)
                End If
            End If
        End Sub
 
        Public Overrides Sub Save()
            If GlobalAppSettings.ViewStatePersister = "SQL" Then
                SaveToDb()
            Else
                SaveToDisk()
            End If
        End Sub

  
Private Function Serialize(viewstatePair As Pair) As Byte()
      Using mem As New MemoryStream()
          formatter.Serialize(mem, viewstatePair)
 
          Return mem.ToArray()
      End Using
  End Function
 
  Private Function Deserialize(buffer As Byte()) As Pair
      Using mem As New MemoryStream(buffer)
          Dim viewstatePair As Pair = DirectCast(formatter.Deserialize(mem), Pair)
 
          Return viewstatePair
      End Using
  End Function


Implementation for example for the file based solution is this one (and it works correctly with viewstate as I can check wit the label "--testme" above)

Private Sub LoadFromDisk(viewStateId As String)
    Dim viewStatePath As String = Path.Combine(GlobalAppSettings.ViewStateStorePath, viewStateId)
 
    If Not File.Exists(viewStatePath) Then
        Throw New Exception("The Viewstate file " & viewStateId & " is missing")
    Else
        Dim viewStateString As String
 
        ' Read the viewstate from disk
        Try
            Dim sr As StreamReader = File.OpenText(viewStatePath)
            viewStateString = sr.ReadToEnd()
            sr.Close()
        Catch ex As Exception
            Throw New ApplicationException("Errore durante il recupero del ViewState dal DB", ex)
        End Try
 
        ' Deserialize the pair viewstate and controlstat
        Dim viewstatePair As Pair = formatter.Deserialize(viewStateString)
 
        ViewState = viewstatePair.First
        ControlState = viewstatePair.Second
    End If
End Sub
 
Private Sub SaveToDisk()
    If ViewState IsNot Nothing OrElse ControlState IsNot Nothing Then
        Dim viewstatePair As New Pair(ViewState, ControlState)
        Dim writer As New StringWriter()
 
        Dim viewStateGuidName As String = System.Guid.NewGuid().ToString()
        Dim viewStatePath As String = Path.Combine(GlobalAppSettings.ViewStateStorePath, viewStateGuidName)
 
        ' Serialize the viewstate and controlstate pair into a base64 encoded string
        formatter.Serialize(writer, viewstatePair)
 
        ' Write the viewstate and controlstate to a text file named after the random GUID
        Try
            Dim sw As StreamWriter = File.CreateText(viewStatePath)
            sw.Write(writer.ToString())
            sw.Close()
        Catch ex As Exception
            Throw New ApplicationException("Errore durante il salvataggio del ViewState su disco", ex)
        End Try
        ' Add the hidden field to the web page with the viewstate random GUID
        Page.ClientScript.RegisterHiddenField("__EVAVS", viewStateGuidName)
    End If
End Sub

The only "odd" think I saw with the debugger is that the Load event of the viewstate is called twice (saved once but called twice) and this shouldn't be the standard behaviour I guess... but I cannot imagine how this could be an issue apart from resource wasting.

Do you have any hint to carry on my debug and track the issue? Some way to peer in the controlstate to check if it's all ok (just in case the screenshot I posted where of no use).

This is very important for me because handling the state off the page allows for a more "liberal" usage of viewstate as a session-like "cache" while keeping page size low of course.

Thanks in advance for any assistance you could provide with this issue.

PS. I'm using this chain of derived classes for my page: BasePage > AdminBasePage > Page + BaseMasterPage > BaseAdminMasterPage > MasterPage. The viewstate persistance implementation resides only in the BasePage.
0
Tsvetoslav
Telerik team
answered on 16 Jul 2013, 02:52 PM
Hello Massimiliano,

Please, open up a formal support ticket and attach a runnable copy of your implementation. We shall debug it and get back to you with more information.
 
Regards,
Tsvetoslav
Telerik
If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to the blog feed now.
0
Massimiliano
Top achievements
Rank 1
answered on 16 Jul 2013, 03:39 PM
Thank you Tsvetoslav
"Runnable" will not be easy since is a very very complex implementation (is a little cms/framework on its own).
When I have some spare hours I will try to strip it down to a single page with minimum and check that it still doesn't work.
In the meanwhile if you have some suggestions on debugging steps (for example how to chek grid controlstate and what should be there in each pagination or postback) it will be faster.
0
Massimiliano
Top achievements
Rank 1
answered on 16 Jul 2013, 03:43 PM
Hallo Tsvetoslav and thank you for your support. Since its a very complex implementation (I'm developing a little framework/cms on its own) when I'll have a couple of spare hours I will try to strip it down to a "single page" where the issue remains (but I'm afraid that stripping it to single page will remove the issue as well).
In the meanwhile if you have suggestion on debugging, on where to place the breakpoints or debug variables, to check for example what is in the controlstate and what should be there at wich time, I'm sure it will be faster. If not I'll try to prepare the solution you asked when I can.
One consistent issue (but it's just one) is during pagination, pushing the "next" page for example.
0
Radoslav
Telerik team
answered on 19 Jul 2013, 07:21 AM
Hi Massimiliano,

Unfortunately without having a simple runnable example where the problem could be replicated it is very hard to say what the reason for the described issue is.  Also the RadGrid ViewState and ControlState are used for many properties (some of them are internal private or protected) and I do not think that we can inspect the states directly. On suggestion here can be to try using PersistenceFramework which is designed for this purpose. More information you can find here:
http://demos.telerik.com/aspnet-ajax/persistence-framework/examples/custom-storage-provider/defaultcs.aspx
http://www.telerik.com/help/aspnet-ajax/persistence-framework-overview.html

Regards,
Radoslav
Telerik
If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to the blog feed now.
0
Massimiliano
Top achievements
Rank 1
answered on 19 Jul 2013, 09:41 AM
Thank you Radoslav for your always kind support.
The persistance control is indeed a great addition to the suite but I see it more as a "long term" or "between session" layout/state storage.
My persister instead moves viewstate/controlstate of every page to the db for obvious benefits.
The viewstate is restoring correctly, I've been able to test it (today I will check the NeedDataSource event in the debugger to see if it fires AFTER the other events thus confirming viewstate is ok).
The control state I'm not able to test but, from the persister code I pasted, it should be straightforward. It's serialized together with the viewstate in a pair, so if one works the other should work as well: Dim viewstatePair As New Pair(ViewState, ControlState)
I'll try to find the time to render a runnable example. I hope the issue remains even in the simplest scenario, this would speed up thing but now I'm 99% confident the problem is with the controlstate (not the viewstate).

Thanks again
0
Massimiliano
Top achievements
Rank 1
answered on 22 Jul 2013, 08:23 PM
I've been able to strip it down a single page in an auto-contained project and the good news is that the issue persists (so you can help me debug it) and also I've been able to track it down exactly to the origin of the problem, but I don't know why this happens.
The problem is somehow tied to AJAX. Infact if you disable ajax manager the grid works as expected with the disk viewstate persister, if you enable AJAX it seems that controlstate is not persisted across AJAX postabacks.
To check this you can simply click on the "NEXT" page button, or just filter a column, then order a column and you will see the filter is reset, and so on with grouping, etc. Each postback seems resets the controlstate.
I'm opening a ticket as per your suggestion, referring back to this topic. I will update the topic when we found the issue for other users benefit.

Thanks
0
Radoslav
Telerik team
answered on 24 Jul 2013, 10:56 AM
Hello Massimiliano,

Could you please try using the StateFormatter when you serialize or deserialize values instead of the LosFormatter:
Private Sub LoadFromDisk(viewStateId As String)
            Dim viewStatePath As String = Path.Combine(AppDomain.CurrentDomain.GetData("DataDirectory").ToString(), viewStateId)
 
            If Not File.Exists(viewStatePath) Then
                Throw New Exception("The Viewstate file " & viewStateId & " is missing")
            Else
                Dim viewStateString As String
 
                ' Read the viewstate from disk
                Try
                    Dim sr As StreamReader = File.OpenText(viewStatePath)
                    viewStateString = sr.ReadToEnd()
                    sr.Close()
                Catch ex As Exception
                    Throw New ApplicationException("Errore durante il recupero del ViewState dal DB", ex)
                End Try
 
                Dim viewstatePair As Pair = StateFormatter.Deserialize(viewStateString)
 
                ViewState = viewstatePair.First
                ControlState = viewstatePair.Second
            End If
        End Sub
 
        Private Sub SaveToDisk()
            If ViewState IsNot Nothing OrElse ControlState IsNot Nothing Then
                Dim viewstatePair As New Pair(ViewState, ControlState)
                Dim writer As New StringWriter()
 
                Dim viewStateGuidName As String = System.Guid.NewGuid().ToString()
                Dim viewStatePath As String = Path.Combine(AppDomain.CurrentDomain.GetData("DataDirectory").ToString(), viewStateGuidName)
 
                Dim ser As String = StateFormatter.Serialize(New Pair(ViewState, ControlState))
               
                ' Write the viewstate and controlstate to a text file named after the random GUID
                Try
                    Dim sw As StreamWriter = File.CreateText(viewStatePath)
                    sw.Write(ser)
                    sw.Close()
                Catch ex As Exception
                    Throw New ApplicationException("Errore durante il salvataggio del ViewState su disco", ex)
                End Try
                ' Add the hidden field to the web page with the viewstate random GUID
                Page.ClientScript.RegisterHiddenField("__EVAVS", viewStateGuidName)
            End If
        End Sub

Also more information about overriding the HiddenFieldPageStatePersister you can find here:
http://stackoverflow.com/questions/1010/how-to-get-the-value-of-built-encoded-viewstate
http://aspguy.wordpress.com/tag/hiddenfieldpagestatepersister/
http://dotnetinside.com/framework/v2.0.50727/framework/v2.0.50727/System.Web/HiddenFieldPageStatePersister

Also please note that this is a custom implementation and it is out of our support scope.

I hope this helps.

Regards,
Radoslav
Telerik
If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to the blog feed now.
0
Massimiliano
Top achievements
Rank 1
answered on 24 Jul 2013, 07:48 PM
I appreciate very much your help. I understand this is out of scope of Telerik support, but I really have to find a way to solve it if I want to have a RadGrid at his full potential ;)
Will be back tomorrow with the testing results. Don't understand why Ajax should interfere with LOS formatter but will read the link you proposed in depth. Thanks again
0
Massimiliano
Top achievements
Rank 1
answered on 25 Jul 2013, 04:32 PM
Here I am. I tried your solution and sadly it didn't work. The problem was not tied to the serializer but somehow to using or not using AJAX.
Infact without AJAX everything works, when I enable RadAjaxManager it doesn't work anymore.
So I focused my search on custom viewstate persister + ajax related issues and luckly come out with a post that provided a working solution.
I think it's right to link the original source to which I feel particularly thankful this time (if this violates Telerik forum rules somehow please remove the whole line/link without problems): http://www.munsplace.com/blog/2007/10/29/custom-pagestatepersister-and-ajax/comment-page-1/

 I guess this will be much useful for anyone implementing a similar solution.

Simply put, instead of registering the hidden field with
Page.ClientScript.RegisterHiddenField("__EVAVS", viewStateGuidName)

It should be registered with
ScriptManager.RegisterHiddenField(Page, "__EVAVS", viewStateGuidName)

That way the script manager is "aware" of the field and keeps it in sync between Ajax requests.
Tested and working.
Tags
Grid
Asked by
Massimiliano
Top achievements
Rank 1
Answers by
Tsvetoslav
Telerik team
Massimiliano
Top achievements
Rank 1
Radoslav
Telerik team
Share this question
or