Search code examples
asp.netvb.netwebforms

Preventing overwriting values one from another of the same application but different windows/tabs with different ID as query string


I have an application, a survey. The way I'm testing it is I opened the survey with first tab, then I opened a new survey with the second tab. On the query string/URL, they have different IDs. I answered all the questions on the first tab but I don't submit yet. Then I'll answer the questions on the second tab, but not submitting it yet. Now, I will submit the answers from my first tab. If I checked on the database, it will get all the answers I put on the second tab. So that means the global variables were overwritten whatever last input I made. How will I prevent this, how do I make sure that I'm opening two surveys at a time with different sessions?

I haven't used Session yet, I'm just assuming that it will be the solution for my problem. Maybe someone could direct me on how to use sessions or suggest other solutions to in preventing my problem.

      Public Class GlobalSessionVariables
        Private _Zone_ID As String = "ZONE_ID"

        Public Property ZONE_ID() As Integer
              Get
                 Return HttpContext.Current.Session(_Zone_ID)
              End Get
              Set(ByVal value As Integer)
                 HttpContext.Current.Session(_Zone_ID) = value
              End Set
        End Property 
      End Class

 Public Function Insert(ByVal zondeID As String) As Boolean

    Try
        'Calling a DB command to store value in the DB
        Result = Insert(_Application_Component_Number, zondeID )

    Catch ex As Exception
    End Try

    Return Result

  End Function

     

  Private BO_Survey As New MaintainBusinessClass(_AppComponentNum)
  Private _GlobalSessionVariables As New GlobalSessionVariables

 Private Sub btnNext_Click(sender As Object, e As EventArgs) Handles btnNext.Click
  Try
        Page.Validate()
        If Page.IsValid Then
            'Get the page 1 Answers
            _GlobalSessionVariables.ZONE_ID = CInt(txtAnswer1.Text) 'ZoneID for Question-1
        End If

  Catch ex As Exception           
  End Try
 End Sub

 Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
 Try
        Page.Validate()
        If Page.IsValid Then
              BO_Survey.Insert(_GlobalSessionVariables.ZONE_ID)
        End If

  Catch ex As Exception           
  End Try
  End Sub

Solution

  • Well, it not at all clear why you have some class to simply place some value into Session, and then pull the value out.

    I mean, why not just go

    Session("ZoneID") = some value
    

    And to get the value back, then just go

     some value = Session("ZoneID")
    

    So, you have to explain why you going to all that trouble to build a class when simple use of Session("some value") should suffice.

    The next issue is having some "ID" in the URL. I recommend you don't use URL query parameters for things like database ID values, since the user can change or type in different values, and that means they may well be able to view or save to an ID that does not belong to the current user.

    So, there are several options to persist values, and session is "global" to the one current user. However, if you build a page that works from some ID value, then if two pages are open, and open to a different ID in session, then you going to have trouble. Those pages may well be based on the current page ID, or the user might even have two or three copies of the browser open, and thus any and all Session() values will be the same.

    However, you can adopt ViewState for storing a few values. ViewState is per page, and not global per user.

    Hence, often I will have a set of variables for a given page, and since using a whole bunch of Session() variables in such pages does not work View State well the VERY instant you have multiple pages open. And I really beyond strongly recommend you don't open multiple browser tabs, since that often means the user will get lost as to what the next step or option to use on a given page, since now you have multiple pages open. In fact, all the rage today is to called SPA's (Single page applications, and here you are now building an application with multiple tabs being open, and that's simply a bad design choice).

    In fact, ask yourself what web application have you used in which multiple tabs are used and opened? For example, you are reading this post on SO, and does it work by opening a new tab for each post you view? Answer = no, it does not.

    However, we still often need a set of persisting values and variables to allow a page to correctly work. And we often still need to pass a set of values from one web page to the next.

    Since ViewState is per web page, and not global to the one user, then I often adopt the following design pattern:

    To pass values from one page to the next, and not have some messy (and not secure) ID values in the URL?

    I use Session() to pass the values to the next page, but on first page load, I then transfer Session() values to ViewState().

    This in near all cases then allows you have multiple pages or tabs open to the one same URL, and allows multiple pages to work correctly, since now the persisted values for that one page are limited to the one web page, and not global Session() to that given user.

    Hence, say I have about 6-10 values that the code behind needs, so often create a simple class for that purpose. I recommend a simple class to hold all such values for a given page, since then you don't have to remember if that Session() value is being used by some other page, and thus breaking such pages to work with multiple copies of that page being open.

    Hence, say this simple class:

    <Serializable>
    Public Class clsProjectInfo
    
        Public ContactID As Integer = 0
        Public ContactGeneralID As Integer = 0
        Public PortalComp As String = ""
        Public ProjectHeaderID As Integer = 0
        Public QuoteNum As Integer = 0
        Public InvoiceNum As Integer = 0
        Public UpLoadGroup As Integer = 0
        Public txtNotes As String = ""
        Public ProofID As Integer = 0
        Public DocketNum As Integer = 0
        Public PONumber As String = ""
        Public ProjectComponentID As Integer = 0
        Public PortalID As Integer = 0
        Public ShipLocatonID As Integer = 0
    
    End Class
    

    So, say in some search page, the user selects a project. I will setup the above values and save into Session().

    Then on the target page we navigate to, I assume then Session() is correctly setup, but as noted, I transfer the Session() value to ViewState(). Now, that page and code behind works from ViewState(), and thus I don't care if the user has 1 or 5 copies of that page open, it will continue to function correctly, and the values used in that page are not Session() and are not global to the one user, but scoped to the one web page in question.

    Hence, you see code like this in my page load event:

    Public cPinfo As New ControlMIS.clsProjectInfo
    
    
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
        If IsPostBack = False Then
            cPinfo = Session("ProjectInfo")
            ViewState("ProjectInfo") = cPinfo
        Else
           cPinfo = ViewState("ProjectInfo")
        End if
    

    Now, any where in code for that page, I'm free to use any and all of my persisted values for that one page.

    Hence:

        GetUserInfo.SetProjectHistory(cPinfo.ProjectHeaderID)
    

    So, I have a current ProjectHeaderID (it is the primary key value from the database).

    Hence, not only do I have a nice grouping of all the values required for the page to operate, I don't mess with Session() or ViewState() over and over on the page. So, once page load has pulled the class from ViewState() (or first time Session, transferring into ViewState()), then all of my required code values for that one web page are persisted, and I simply use cPinfo in that code behind:

    enter image description here

    So, not only can I now pass a hodge podge set of values to the next page in question? Those values having been transferred from Session() to ViewState() means now that page does not use Session() anymore, but uses ViewState(), which as I pointed out is "per web page", and not "per web user".

    And note in the above screen capture, how I have about 8 values that I can pass to the next web page, and I have ZERO need to place such values in the URL, which as I pointed out are often VERY dangerous, since the user is free to change values in that URL, and if they change the ID value in your URL, now your page breaks, but far worse is changing that ID can often result in the user now working on someone's else's data.

    So, for database ID values, they should not be exposed in the URL, since the user with great ease can change that ID value. So, pass values between pages using Session(). And if you have a bunch of values to pass, then create a class, and use Session() to pass that class and "bunch" of variables to the next page. Then on page load, transfer into ViewState(), and now you have a persisted set of values scoped to the one page. The result is the user can open 5 copies of their browser, and all copies on the same URL will work just fine, since ViewState is scoped to the one page, not the user.

    Do use some caution, since ViewState() saves the values into the client-side browser, where as Session() is server based. So, don't place huge objects, or even table data into ViewState(), since such values become part of the page post-back, and thus large ViewState() objects will hurt performance in a significant way.

    So, adopt the above design pattern.

    Hence, for one value, just use Session("SomeValue") = some value.

    For a bunch of values, create a class, and pass that.

    And then on first page load, transfer the Session() values (our class) into ViewState(), and now you have per page persisting of such values for code behind to use.