Persisting ViewState and ControlState to DB/Disk breaks functionality

12 posts, 0 answers
  1. Massimiliano
    Massimiliano avatar
    184 posts
    Member since:
    Oct 2012

    Posted 08 Jul 2013 Link to this post

    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?


  2. Tsvetoslav
    Admin
    Tsvetoslav avatar
    1823 posts

    Posted 11 Jul 2013 Link to this post

    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.
  3. UI for ASP.NET Ajax is Ready for VS 2017
  4. Massimiliano
    Massimiliano avatar
    184 posts
    Member since:
    Oct 2012

    Posted 11 Jul 2013 Link to this post

    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.
  5. Tsvetoslav
    Admin
    Tsvetoslav avatar
    1823 posts

    Posted 16 Jul 2013 Link to this post

    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.
  6. Massimiliano
    Massimiliano avatar
    184 posts
    Member since:
    Oct 2012

    Posted 16 Jul 2013 Link to this post

    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.
  7. Massimiliano
    Massimiliano avatar
    184 posts
    Member since:
    Oct 2012

    Posted 16 Jul 2013 Link to this post

    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.
  8. Radoslav
    Admin
    Radoslav avatar
    1564 posts

    Posted 19 Jul 2013 Link to this post

    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.
  9. Massimiliano
    Massimiliano avatar
    184 posts
    Member since:
    Oct 2012

    Posted 19 Jul 2013 Link to this post

    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
  10. Massimiliano
    Massimiliano avatar
    184 posts
    Member since:
    Oct 2012

    Posted 22 Jul 2013 Link to this post

    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
  11. Radoslav
    Admin
    Radoslav avatar
    1564 posts

    Posted 24 Jul 2013 Link to this post

    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.
  12. Massimiliano
    Massimiliano avatar
    184 posts
    Member since:
    Oct 2012

    Posted 24 Jul 2013 Link to this post

    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
  13. Massimiliano
    Massimiliano avatar
    184 posts
    Member since:
    Oct 2012

    Posted 25 Jul 2013 Link to this post

    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.
Back to Top
UI for ASP.NET Ajax is Ready for VS 2017