Problem retrieving multiple instances of the same asp.net session variable - asp.net

I'm having problems with retrieving multiple instances of a session variable from an InProc session state.
In the following code I persist a simple BusinessObject into a session variable on the Page_Load event. On the click of a button I try to retrieve the object back into 2 new declared instances of the same BusinessObject.
All works great until I change one of the properties in the first instance, it changes the second instance as well.
Is this normal behaviour? I would have thought as these were new instances they wouldn’t demonstrate static behaviour?
Any ideas where I'm going wrong?
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
' create a new instance of a business object and set a containg variable
Dim BO As New BusinessObject
BO.SomeVariable = "test"
' persist to inproc session
Session("BO") = BO
End If
End Sub
Protected Sub btnRetrieveSessionVariable_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnRetrieveSessionVariable.Click
' retrieve the session variable to a new instance of BusinessObject
Dim BO1 As New BusinessObject
If Not Session("BO") Is Nothing Then BO1 = Session("BO")
' retrieve the session variable to a new instance of BusinessObject
Dim BO2 As New BusinessObject
If Not Session("BO") Is Nothing Then BO2 = Session("BO")
' change the property value on the first instance
BO1.SomeVariable = "test2"
' why has this changed on both instances?
Dim strBO1Property As String = BO1.SomeVariable
Dim strBO2Property As String = BO2.SomeVariable
End Sub
' simple BusinessObject class
Public Class BusinessObject
Private _SomeVariable As String
Public Property SomeVariable() As String
Get
Return _SomeVariable
End Get
Set(ByVal value As String)
_SomeVariable = value
End Set
End Property
End Class

your BO1 and BO2 are the same object
BO1 is a name that references some area in memory;
BO2 is another name that references the SAME area of memory; Session("BO") references the SAME area of memory.
To truly create different objects BO1 and BO2, you should create a copy of the object - for example implement Clone() method in your business object class.

You're instantiating two new objects, and then setting each of them to be the same object (i.e. the one from session), so your behaviour is exactly as you would expect.
Incidentally, you may wish to consider how your page would perform if a user opens two of these pages in a tab - will your business object in the session then cause you some problems?

Related

Dataset is NULL after clicking form button

Dataset is NULL after clicking form button.
I want to learn and understand Visual Basic 2017.
On a test webform some textboxes are filled with dataset (DS) items (during pageload).
This works well without problems .
There is also a SAVE button on the form that must fill a textbox with a dataset item after clicking.
But then the dataaet appears to be to be NULL.
How is that possible?
I hope someone can tell me what is wrong, I understood thata dataset stays dukkws while running.
Aftr clicking the SAVEbutton this error appears: ==========================================
System.NullReferenceException: Object reference not set to an instance of an object.
at NW_DB_update_test.NWtest.testDS() in S:\P2 S VS tests\NW DB
update test\NW DB update test\NWtest.aspx.vb:line 46
Thw VB soutce is ==================================
Option Explicit On
Option Strict On
Imports System.Data.SqlClient
Public Class NWtest
Inherits System.Web.UI.Page
Public DA As SqlDataAdapter
Public DS As System.Data.DataSet
Public CB As SqlCommandBuilder
Public Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
Call GETNWDS()
Call testDS()
End If
End Sub
Public Sub GETNWDS()
'' opencustumorDataAdapterdaaset DS
Dim connectionString = ConfigurationManager.ConnectionStrings("NW testConnectionString").ConnectionString
Dim queryString As String = "SELECT CustomerID, CompanyName, ContactName FROM Customers where CustomerID='ALFKI'"
Dim conn As New SqlConnection(connectionString)
DA = New SqlDataAdapter(queryString, conn)
DS = New System.Data.DataSet
DA.Fill(DS)
CustomerIDbox.Text = DS.Tables(0).Rows(0).Item("CustomerID").ToString
CompanyNamebox.Text = DS.Tables(0).Rows(0).Item("CompanyName").ToString
ContactNamebox.Text = DS.Tables(0).Rows(0).Item("ContactName").ToString
End Sub
Public Sub testDS()
Try
Dim v As String
v = "in DS= " + DS.Tables(0).Rows(0).Item("ContactName").ToString
Textbox.Text = v
'Error after clicking the save button:
' System.NullReferenceException
' Object reference Not set to an instance of an object.
' at NW_DB_update_test.NWtest.testDS() in S: \P2 S VS tests\NW DB update test\NW DB update test\NWtest.aspx.vb:line 46
Catch x As Exception
Textbox.Text = x.ToString
End Try
End Sub
End Class
========================================================================================
I am not sure what you are trying to achieve, but as ADyson explained, the variables you have declared on page load are non-existent after the postback. On the other hand, if you just want to read the ContactName, you can get it directly from the textbox it was written to, as controls do persist between postbacks, if not otherwise stated.
Textbox.Text=ContactNamebox.Text

How to persist an object in webform

As I'm self taught my VB coding is not bad but my use of OOP is poor. I'm sure this can be done but I have not found out how yet.
I am building a webforms app which needs to grab data about a user from AD. I have a Person Class which I can use as follows
Public Class _Default
Inherits System.Web.UI.Page
Dim LoggedOnPerson As Person 'Added here so available throughout class
Private strLoggedOnUser As String
Private strADDomain As String
Private strADUserID As String
Public Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
strLoggedOnUser = Request.ServerVariables("LOGON_USER").ToUpper
strADDomain = strLoggedOnUser.Split("\")(0)
strADUserID = strLoggedOnUser.Split("\")(1)
If Not IsPostBack Then
'Dim LoggedOnPerson As Person *** changed to
LoggedOnPerson = New Person
'Get details from AD for logged on user
LoggedOnPerson.GetDetails(strADDomain, strADUserID)
'Store in Session
Session("LoggedOnUser") = LoggedOnUser
'This will now give me access to details such as
'LoggedOnPerson.EmailAddress
'LoggedOnPerson.GivenName
'LoggedOnPerson.TelephoneNo etc.
Else
'Postback so pull in details from Session
LoggedOnUser = Session("LoggedOnUser")
End If
End Sub
End Class
My problem is that I cannot access LoggedOnPerson in other events. e.g.
Public Sub SaveDetails()
Dim email As String = LoggedOnPerson.Email
'This now produces correct result. No error that LoggedOnPerson is not declared
End Sub
I of course get LoggedOnPerson is not declared error. How can I get around this.
You have created the object of "Person" inside Page_Load event. Take it outside and declare at the class level. Also add that object to view state/session state on Page_Load event and typecast it to "Person" class inside other events.

Help with refreshed ASP.NET page clearing public array

Hey all, i am new at everything VB.net/ASP.net so i need some help with a problem i am currently having.
I have an ASCX.vb page that lets the user choose someone from a table. Once they do select someone, the page reloads with that persons information into another table below that one where they can submit an order for that person or remove that person.
Problem being is that i wanted to store the users ID that are selected but it seems that everytime the page reloads to show an update, it dim's my array again back to default.
This is my code once they choose a user:
Namespace prog.Controls
Partial Public Class progReportGrid
etc....
Public strIDArray() As String = {"0000770000"} 'Just a dummy place holder
Private Sub gvData_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles gvData.RowCommand
Dim idIndexNumber As Integer = Array.IndexOf(strIDArray, strID)
For i As Integer = 0 To strIDArray.Length - 1
System.Diagnostics.Debug.WriteLine(strIDArray(i))
Next
If idIndexNumber = -1 Then
ReDim Preserve strIDArray(strIDArray.Length)
strIDArray(strIDArray.Length) = strID
RaiseEvent EmployeeSelected(Me, New ESEventArgs(strID))
End If
End Sub
So everytime to page reloads the Public strIDArray() As String = {"0000770000"} gets called again and, of course, clears anything that was saved to it other than 0000770000.
How can i keep it from doing this?
UPDATE
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
'do what?
End If
End Sub
David
Perhaps you have a misunderstanding with the stateless model of a web application. Anything that is not stored in the ViewState or Session or a shared application variable will NOT persist through postbacks.
This means that when you declare your
Public strIDArray() As String
as a standard variable, it will be re initialized every time the page posts back.
For example, here's a simple flow of how this works:
The user opens their browser and opens up your aspx web page.
The request goes to your server, and all the controls including progReportGrid are instantiated as new instances and added to the page. (this is where your variable gets set to its original value every time)
A bunch of events are fired, including the Me.Load event
Controls that were added to the page are asked to generate their HTML
The HTML gathered from all the controls on the page is sent back to the user
So because your controls are all re-instantiated every post back, class variables like strIDArray are pretty much thrown out after the page is sent to the user.
Instead, if you want the page to remember what value the array had last postback and add more to it next postback, you have to use either ViewState, or Session.
For example:
Private Property StrIDArray() As String()
Get
If ViewState("StrIDArray") Is Nothing
ViewState("StrIDArray") = New String() {"0000770000"}
Return ViewState("StrIDArray")
End Get
Set(ByVal value As String())
ViewState("StrIDArray") = value
End Set
End Property
Now if you used this property instead of your variable, you could manipulate that array and as long as the user is on that page, the value of that array will persist across postbacks.
Use the Page.IsPostBack property in your Page_Load method.
This property is false for the first time the page loads and false for every postback afterwards.
If you set your default value strIDArray() = {"0000770000"} within If (Page.IsPostBack <> true) then it will not get reset on the postback.
EDIT : My VB syntax is VERY shaky but let me try it out
Partial Public Class EARNReportGrid
Begin
Public strIDArray() As String // DO NOT INITIALIZE it here. ONLY DECLARE IT
.....
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
//INITIALIZE the ARRAY
strIDArray = New String(){"00007700000"}
End If
End Sub
......
End

Preserve Data-structure on AJAX postback

Partial Class ClientCenter_UpdateSub
Inherits System.Web.UI.Page
Structure PInfo
Dim Name As String
Dim Surname As String
End Structure
Dim OldPInfo As New PInfo
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
'blah blah
OldPInfo.Name = Dt.Rows(0).Item("Name").ToString
OldPInfo.Surname = Dt.Rows(0).Item("Surname").ToString
end if
end sub
End Class
The first time the page loads my structrure is filled correctly.
After an AJAX postback all the structure fields are setting to nothing. (It seems that the Dim OldPInfo As New PInfo is called again), but i should better ask the SO Experts.
So anyway, what am i doing wrong here?
First off, You should never assign a variable outside of a property or a method.
Second, web applications are stateless (which means NOTHING is automatically saved from call to call - unless you store it somewhere like Viewstate, Session, etc.).
Remember to accept this answer if it helps solve your problem.

How to Persist Variable on Postback

I created a single page (with code behind .vb) and created Public intFileID As Integer
in the Page load I check for the querystring and assign it if available or set intFileID = 0.
Public intFileID As Integer = 0
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
If Not Request.QueryString("fileid") Is Nothing Then
intFileID = CInt(Request.QueryString("fileid"))
End If
If intFileID > 0 Then
GetFile(intFileID)
End If
End If
End Sub
Private Sub GetFile()
'uses intFileID to retrieve the specific record from database and set's the various textbox.text
End Sub
There is a click event for the Submit button that inserts or updates a record based on the value of the intFileID variable. I need to be able to persist that value on postback for it all to work.
The page simply inserts or updates a record in a SQL database. I'm not using a gridview,formview,detailsview, or any other rad type object which persists the key value by itself and I don't want to use any of them.
How can I persist the value set in intFileID without creating something in the HTML which could possibly be changed.
[EDIT] Changed Page_Load to use ViewState to persist the intFileID value
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
If Not Request.QueryString("fileid") Is Nothing Then
intFileID = CInt(Request.QueryString("fileid"))
End If
If intFileID > 0 Then
GetFile(intFileID)
End If
ViewState("intFileID") = intFileID
Else
intFileID = ViewState("intFileID")
End If
End Sub
As others have pointed out, you can store it in the Session or the ViewState. If it's page specific, I like to store it in the ViewState as opposed to the Session, but I don't know if one method is generally preferred over the other.
In VB, you would store an item in the ViewState like:
ViewState(key) = value
And retrieve it like:
value = ViewState(key)
Store in:
Session
ViewState
Hidden input
Just to summarize what is said above.
You can use Session, Viewstate, or a hidden field.
I personally prefer viewstate as it will work in web farm environments, Session does not, it does not store it on the server waiting for the user, for up to 20 minutes to be removed, and in general viewstate is the place to be for page level data.
You can use a hidden field, but then a user could more easily modify it.
Store it in the Session.
Page.Session["MyPage_FileID"] = intFileID
You'll need to have logic that manages it as the user navigates around, but if it is always set when the page loads from a GET (or you clear it, if not available on GET) then you should be ok using it later from the Session on your submit PostBack.
Remember:
Each time your server code runs, it's in a brand new instance of your page class. That's for every postback.
Actually, since an ASP.NET page postbacks to itself - including the query string - you could just remove the If Not Page.IsPostBack condition. Then it'd set itself on each postback.
I personally would choose to store the value in control state instead of viewstate as viewstate can easily be switched off. ControlState will persist even if viewstate is switched off for any reason. I have included an example on how this may be done.
Private intFileId As Integer = 0
Public Property FileID() As Integer
Get
Return intFileId
End Get
Set(ByVal value As Integer)
intFileId = value
End Set
End Property
Protected Overrides Function SaveControlState() As Object
Dim objState(2) As Object
objState(0) = MyBase.SaveControlState()
objState(1) = Me.FileID
Return objState
End Function
Protected Overrides Sub LoadControlState(ByVal savedState As Object)
Dim objState() As Object
objState = savedState
MyBase.LoadControlState(objState(0))
Me.FileID = CInt(objState(1))
End Sub
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
Me.Page.RegisterRequiresControlState(Me)
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not Page.IsPostBack Then
If Not String.IsNullOrEmpty(Request.QueryString("fileid")) Then
Me.FileID = CInt(Request.QueryString("fileid"))
End If
End If
Response.Write(Me.FileID.ToString())
End Sub
Session["KeyName"] = your value;
Type cast to the type to retrieve and store the data from session like given below :
Datatable dt = (DataTable)(Session["KeyName"]);
or
ViewState["KEY"]= value;
Type cast to the type to retrieve and store the data from session like given below :
String str = (String)ViewState["KEY"];
I'll use Session as suggested by tvanfosson.
ViewState and HiddenField might be too heavy if you want to keep large data like a dataset for comments in a forum's topic pages..

Resources