Trying to get Session value in Global.asax, but it is NULL - asp.net

I have a session which contains a List(of String) which I create on Main.aspx. In my Global.asax I execute the following code to retrieve the Session value after a Timer is fired.
Private Sub Application_AcquireRequestState(sender As Object, e As EventArgs)
If System.Web.HttpContext.Current.Session IsNot Nothing Then
If Session("Products") IsNot Nothing Then
Response.Write(Session("Products").ToString())
End If
End If
End Sub
However, Session("Products") returns Null and the Sub is exited. I wonder if End Sub in Main.aspx releases the Session and therefore whenever the Timer fires, the Session is empty. Any thoughts?

In your Main.vb code behind, be sure to inherit the System.Web.SessionState.IRequiresSessionState interface in addition to whatever is being inherited.
Something like this should work:
Public Class Main
Inherits System.Web.UI.Page, System.Web.SessionState.IRequireSessionState
' Your application code
End Class
This is strange behavior though as ASP.NET webpages should already handle session data. Usually, you inherit IRequireSessionState on generic handlers (.ashx) since they don't support session data by default.

Related

What type of variable could I use for this?

What I am trying to do is simple in principle, but the lifecycle of ASP.NET pages is throwing a bucket of cold water into my day.
Here is the problem
We have implemented URL Redirection and I've inherited code that reads like this in Global.ASAX, on Sub Application_BeginRequest:
dv.Table = CommonFunctions.ConvertXmlFileToDataSet("/XmlData/WebAppFolders.xml").Tables("Application")
dv.RowFilter = "'" + fullOrigionalPath + "' LIKE '%'+ folder + '%'"
If dv.Count > 0 Then 'match on key to redirect
If fullOrigionalPath.EndsWith(dv(0)("folder") + "/") Then
Context.RewritePath(dv(0)("basePage"), True)
Else 'missing page in directory --> redirect
HttpContext.Current.Items("Raise404") = "true"
Response.Redirect("/" + dv(0)("folder"))
End If
Return
End If
Basically we are reading a large XML file that contains the URL redirects. That's working fine. The problem happens in the line...
HttpContext.Current.Items("Raise404") = "true"
In the context of Application_BeginRequest, the session object is not yet available, so I could not use it to store a flag which I named Raise404. Instead I had to resort to using the Items collection.
The problem occurs when the redirect takes place. The new page lifecycle destroys the Items array and overwrites it with a new empty one.
By the time I try to use my flag Raise404, it no longer exists on my page PreRender event.
To complicate matters we use master pages, and so I was asked to place the code that we want to execute in the master page
Ideally, if the Items array wasn't being destroyed, this code would work:
Private Sub Page_PreRender(sender As Object, e As System.EventArgs) Handles Me.PreRender
If HttpContext.Current.Items("Raise404") IsNot Nothing AndAlso HttpContext.Current.Items("Raise404").Equals("true") Then
Response.StatusCode = 404
HttpContext.Current.Items("Raise404") = Nothing
End If
End Sub
I am not sure what kind of variable I could use to store my flag and allow it to survive the redirect.
Any ideas?
Update: The problem is that the HTTP Handler servicing my request is System.Web.DefaultHTTPHandler, which does not implement IRequiresSessionState, and so when my request is being handled inside Global ASAX there is no session created. So, it seems like the solution will be to write a custom HTTPHandler that implements IRequiresSessionState, and use that for all my .aspx files. Even then, a session state is not created in Global.ASAX until PreRequestHandlerExecute is raised. So, putting it all together I think I need to write a custom HTTP Handler that implements IRequiresSessionState, and delay the redirection of the page until PreRequestHandlerExecute is raised where I will be able to store my flag into the Session state, and only after that, redirect my page.
Not very elegant, and I wonder if there will be any performance implications.
Have you considered using the query string for that?
Response.Redirect("/" & dv(0)("folder") & "?Raise404=true")
Then, in your Master Page, simply check QueryString("Raise404") and act accordingly.
The only drawback I can see is that a malicious client could deliberately add Raise404=true to the query string, which is not possible with your current solution. However, I do not see how that could do any harm.
As I suspected, my problem was directly tied to the fact that IIS 8 will avoid using an HTTPHandler that implements IRequiresSessionState if for performance reasons it detects that a session object is not needed, and so because the redirect code was happening during the Application_BeginRequest event, IIS was handling up to that point my requests using System.Web.DefaultHTTPHandler.
So, upon further research, I found this which was tremendous help:
IRequiresSessionState - how do I use it?
And I also documented myself on this article:
http://forums.iis.net/t/1094546.aspx
What solved my problem was to write a dummy HTTPHandler class. I added a new class to my app_code folder:
Imports System.Web
Public Class HTTPRequestHandler : Implements IHttpHandler, IRequiresSessionState
Private OriginalHandler As IHttpHandler
Public Sub New(handler As IHttpHandler)
Me.OriginalHandler = handler
End Sub
Public Sub ProcessRequest(context As HttpContext) Implements IHttpHandler.ProcessRequest
Throw New InvalidOperationException("HTTPRequestHandler cannot process requests.")
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
Public ReadOnly Property Handler() As IHttpHandler
Get
If Me.OriginalHandler IsNot Nothing Then
Return Me.OriginalHandler
End If
Return HttpContext.Current.Handler
End Get
End Property
End Class
Now, during Application_BeginRequest, session is not available but HttpContext.Current.Items is available. So, as the second article suggests, during PostMapRequestHandler I put a condition:
If HttpContext.Current.Items("Raise404") IsNot Nothing Then
Context.Handler = New MyNameSpace.HTTPRequestHandler(Context.Handler)
End If
And finally, that created the session object that I could finally use during PreRequestHandlerExecute:
HttpContext.Current.Session("Raise404") = "true"
Response.Redirect(HttpContext.Current.Items("Redirect"))
I will definitely not argue that this is not elegant, but it works and I can raise my 404 error during Page_PreRender on my master page.
I hope this may help others.
Cheers.

IE Session.Abandon()

IE seems a bit buggy when it comes on Abondoning the session. This is the whole code thats get executed:
Protected Sub logout_OnClick(ByVal sender As Object, ByVal e As EventArgs)
Session.Abandon()
Response.Redirect("login.aspx")
End Sub
It redirects to login.aspx but when i change the url to default.aspx its get in without checking it. In all the other browsers it doesnt and get you redirected because of the following code:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
If (Session("Naam") Is Nothing) Then
Response.Redirect("login.aspx")
Else
Label1.Text = "Welkom " + Session("Naam").ToString()
End If
End Sub
Is there any reason that IE doesnt abandon the session?
note//
I am not using log-incontrol what so ever
Try disabling/removing the autopostback and then re-test your page. I would guess that the autopostback is somehow keeping your session data active,maybe its being used in the postback? therefore all instance's dont get abandoned?.
Edit
When the Abandon method is called, the current Session object is queued for deletion but is not actually deleted until all of the script commands on the current page have been processed. This means that you can access variables stored in the Session object on the same page as the call to the Abandon method but not in any subsequent Web pages.
For example, in the following script, the third line prints the value Mary. This is because the Session object is not destroyed until the server has finished processing the script.
<%
Session.Abandon
Session("MyName") = "Mary"
Reponse.Write(Session("MyName"))
%>
If you access the variable MyName on a subsequent Web page, it is empty. This is because MyName was destroyed with the previous Session object when the page containing the previous example finished processing.
The server creates a new Session object when you open a subsequent Web page, after abandoning a session. You can store variables and objects in this new Session object.
The above is from the MSDN website.
Which all means that somewhere your session object is being used AFTER you have abandoned it.
I normally use the same code block as you do to log a member out of a session.
Session.Abandon()
Response.Redirect("default.aspx")
with the same code in the global.asax or global.aspx file too.

Storing and restoring properties in ASP.NET derived control

I have created an ASP.NET class derived from the standard WebControls.TextBox, with the intention of adding extra properties that will persist between post-backs. However, I cannot figure how to get the values in these properties to persist.
I have tried setting the value of the properties into the controls ViewState as part of the PreRender handler, but the value is then not accessible in the Init handler on the post-back, because the ViewState has not yet been setup.
I could look for the ViewState value in the Load handler of the control, but if the page/usercontrol that is using the control asks for the properties value during its Load handler, the control hasn't yet reached it's Load handler, and it therefore not there.
My current class looks something like this...
Public Class MyTextBox
Inherits TextBox
Private _myParam As String = ""
Public Property MyParam As String
Get
Return _myParam
End Get
Set(value As String)
_myParam = value
End Set
End Property
Private Sub MyTextBox_Init(sender As Object, e As EventArgs) Handles Me.Init
If Page.IsPostBack Then
_myParam = ViewState("myParam")
End If
End Sub
Private Sub MyTextBox_PreRender(sender As Object, e As EventArgs) Handles Me.PreRender
ViewState("myParam") = _myParam
End Sub
End Class
I must be missing something really simple, such as whether there is an attribute I can set against the property.
UPDATE
Thanks to #AVD pointing out that I really had very little clue about the ViewState and the Init / Load stages, I finally figured it all out.
If you have the time (and if you're doing any major ASP.NET work, you need to make the time) please read the Understand ASP.NET View State document that #AVD pointed me to. It will explain a lot.
However, what it didn't explain is if you place your control within a <asp:Repeater>, you may as well throw all the rules out of the window... and that is exactly the problem I was experiencing.
In the end, the way I managed to get it to work was to use a <asp:PlaceHolder> control within the repeater, create an instance of my control within the ItemDataBound handler of the repeater, and then add the control to the <asp:PlaceHolder>... all done within the Init section (which fortunately I'm able to do).
As Andrew found out in this previous question you can end up in a chicken/egg situation, where you need to create the controls in the Init, but you won't know what controls you need until the Load.
(I have still made AVD's answer the correct one, because in the context of my original question, it is absolutely correct).
You have to store/retrieve value to/from ViewState within the properties accessors.
Public Property MyParam As String
Get
If IsNothing(ViewState("myParam")) Then
return string.Empty
End IF
Return ViewState("myParam").ToString()
End Get
Set(value As String)
ViewState("myParam") = value
End Set
End Property

How to set master page on page preinit based on custom user control method result

I have a user control that checks if a certain query string and session value are present then returns a boolean based on that, if it's true I want to set the master page.
The page is throwing an Object reference exception when it tries to call the method EditUser1.UserAuthorization(). Why is this happening? I imagine that the method doesn't exist at that point in the stack.
Protected Sub Page_PreInit(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreInit
If EditUser1.UserAuthorization(True) Then
Page.MasterPageFile = "APERSEmpCont.master"
End If
End Sub
I just found out from here, that the page controls haven't been initialized at the point of the preinit, so that method doesn't exist at that moment. I'll have to move the method to the page level to make it work.

How can I share a variable from sub routine to sub routine?

I have a sub routine where I pull in information from a database. I want to use variables from this sub routine in another sub routine. I tried making everything public but it doesn't want to be friendly and share.
Dim strEmail as String
Public Sub readDB()
strEmail = "whatever#doohikcy.com"
End Sub
Public Sub submit_btn(ByVal sender As Object, ByVal e As EventArgs)
Response.Write(strEmail)
End Sub
The readDB would read the database and set the variables and do whatever it needs to do. Then when they submit the form it would e-mail the form to whatever the email address was.
Every time an asp.net page loads, the global variables are wiped out. You can avoid this by using Session variables.
Public Sub readDB()
Session("strEmail") = "whatever#doohikcy.com"
End Sub
Then:
Public Sub submit_btn(ByVal sender As Object, ByVal e As EventArgs)
Response.Write(CStr(Session("strEmail")))
End Sub
Passing them as arguments is great, if the sequence of calls to the subs in question makes that feasible. If you literally need a variable that is visible to different subs in the same class (code-behind page in ASP.Net), what you are looking for is probably a private member variable. Declare it outside of any sub or function with the access modifier Private, and all the subs in the class will be able to access it.
Private _foo As String
The underscore is a convention that some people love, some hate. It comes in handy in VB if you want to define a property to expose the variable, where you can't use Foo as distinctly different from foo, but that's another story.
This is not the same as what would generally be understood by the term Global variable in the ASP.Net sense, where the variable would be visible throughout the application context, which lends itself to unintended consequences in the least. A private member variable is only visible to the class that owns it.
EDIT: Your example code was added after my initial answer. My VB is a little rusty but as you've written it, strEmail looks like it should have class-level visibility, including inside of submitbtn (someone correct me if I'm wrong). One possibility, since you mentioned that you are calling readDB in Page_Load is if you're checking for postback in page load, and only calling readDB on the initial load, not on postback, which would be the case when the button is clicked. You may have seen examples that include a check for Postback out of hand and not realized what it does (I only suggest that because you mentioned that you are new to ASP.Net and it's not intuitive if you're new to it--no offense intended).
Protected Sub Page_Load (sender as object, e as EventArgs)
If Not IsPostback
// this doesn't get called when the button is clicked so
// strEmail would not be populated when submitbtn is invoked
readDB
End If
End Sub
That's a bit of a guess out of nowhere though, so it might be way off base. Have you set break-points in Page_Load, readDB and submitbtn to see the state of strEmail in each?

Resources