A website I am working on is very data centric. Some reports take more than an hour to complete. Whenever a user submits a request for a report, a new thread is created which generates the report. The user is then redirected to a page which says that the report in progress, and to please refresh to download the report. If the user again refreshes the page and the report is still in progress, the same message is shown; otherwise a download link is provided.
All report/user relations are saved in the application variable. That works fine, except when the user is inactive for more than 20 min (while the report is being processed), and then the user is logged out; if the user logs in again, the report can still be downloaded.
I do not want to increase the session expiration time, but I need to stop the expiration if the user has something going in background, like a report being processed.
In Session_End I am able to retrieve the the userid and match it in Application["work"] to see the user has pending work or not.
However, I am clueless as to how I can defer the session end in the above case?
Edit: Every one has suggested as a workaround from 'maintaining a contact' to 'using query string'. 'Maintaining the contact' looked the most promising to me but it fails in the following scenarios: a. When browser is closed/computed goes in standby mode during lunch, etc. b. When user goes to another non-asp.net section (it's a legacy site).
Isn't It possible to cancel the Session_End event itself?
The short answer
There is currently (that I know of) no simple way to extend the life of a single ASP.NET session. There is one possible solution: use a custom Session-State Store Provider!
The long answer
First things first: Start with something that is already built! Use the sample Session-State Store Provider (and its tutorial) provided by Microsoft. This sample Session-State Store Provider uses Microsoft Access as its back end; although, because it uses ODBC connections, you can have virtually any database back end supported through your installed ODBC drivers.
This sample Session-State Store Provider is simply a custom version of what ASP.NET uses internally (with the exception that ASP.NET's runs in-memory).
Secondly: Let's prepare the Access Database requirements, and the configuration.
Create the table as specified in the tutorial and in the comments of the file:
CREATE TABLE Sessions
(
SessionId Text(80) NOT NULL,
ApplicationName Text(255) NOT NULL,
Created DateTime NOT NULL,
Expires DateTime NOT NULL,
LockDate DateTime NOT NULL,
LockId Integer NOT NULL,
Timeout Integer NOT NULL,
Locked YesNo NOT NULL,
SessionItems Memo,
Flags Integer NOT NULL,
CONSTRAINT PKSessions PRIMARY KEY (SessionId, ApplicationName)
)
NOTE: If you want to use SQL Server, simply replace Text(...) with varchar(...), YesNo with bit, and Memo with varchar(MAX).
Add/update your web.config with the following (you can use connectionstrings.com to help you generate a connection string):
<configuration>
<connectionStrings>
<add name="OdbcSessionServices" connectionString="DSN=SessionState;" />
</connectionStrings>
<system.web>
<sessionState
cookieless="true"
regenerateExpiredSessionId="true"
mode="Custom"
customProvider="OdbcSessionProvider">
<providers>
<add name="OdbcSessionProvider"
type="Samples.AspNet.Session.OdbcSessionStateStore"
connectionStringName="OdbcSessionServices"
writeExceptionsToEventLog="false" />
</providers>
</sessionState>
</system.web>
</configuration>
Third: Adding a function that will extend for more than the specified Timeout.
Make a copy of the ResetItemTimeout function, and name it ResetItemTimeout2:
var ExtendedTotalMinutes = 2 * 60; // hours * minutes
public override void ResetItemTimeout2(HttpContext context, string id)
{
OdbcConnection conn = new OdbcConnection(connectionString);
OdbcCommand cmd =
new OdbcCommand("UPDATE Sessions SET Expires = ? " +
"WHERE SessionId = ? AND ApplicationName = ?", conn);
cmd.Parameters.Add("#Expires", OdbcType.DateTime).Value
= DateTime.Now.AddMinutes(ExtendedTotalMinutes); // IMPORTANT!! Set your total expiration time.
cmd.Parameters.Add("#SessionId", OdbcType.VarChar, 80).Value = id;
cmd.Parameters.Add("#ApplicationName", OdbcType.VarChar, 255).Value = ApplicationName;
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (OdbcException e)
{
if (WriteExceptionsToEventLog)
{
WriteToEventLog(e, "ResetItemTimeout");
throw new ProviderException(exceptionMessage);
}
else
throw e;
}
finally
{
conn.Close();
}
}
Fourth: Supporting the extension of a single ASP.NET Session!
Whenever you need to extend a session, call the ResetItemTimeout function as follows:
using Samples.AspNet.Session;
// from inside a User Control or Page
OdbcSessionStateStore.ResetItemTimeout2(this.Context, this.Session.SessionID);
// --or--
// from anywhere else
OdbcSessionStateStore.ResetItemTimeout2(System.Web.HttpContext.Current, System.Web.HttpContext.Current.Session.SessionID);
Footnotes
Read the comments on the page with the sample Session-State Store Provider;
There is one potential good entry about a Mistake in GetSessionStoreItem when using GetItem.
Another good one is that Timestamps should be UTC.
There are obvious performance/maintainability improvements that could be done (especially with having duplicate code in ResetItemTimeout and ResetItemTimeout2).
I have not tested this code!
Edits
I realized I missed the part where you want to extend more than the Timeout - answer has been fully updated.
Added footnotes section.
Maintain a contact with the server will avoid Session Timeout.
Create a empty Web service and run that in your server then call the web service by your site by JQuery by the interval of some seconds is enough to keep the session alive
May be this solution will help you..
Try this link for full details : Prevent Session Timeout in ASP.NET
While desperately looking for defering the session_end event I think it seems impossible?
The easiest work around that I was able to come with was 'Using Cookies' and modifying my authentication logic.
I implemented a method for writing a guid key to the cookie named admin when ever user requested for the report with expiration of 9 hour(Max time office will be open for work).
I save this guid with user id in a seperate table.
In the master page where i was checking for session userid I implemented another method to check for any cookie named admin. If it is found i set session to the user id saved in table else i redirect them to login page as before it was happening.
It seems to work like magic. But I need to know is this a right thing?
What you can do is set the Session timeout to a higher value when you detect that a report has been requested that will take a long time. This of course supposes that you can calculate whether a report will take a long time to run. If so, you can do this before you kick off the thread:
Session.Timeout = 120 // set timeout to two hours for this session only
Apart from pinging a page or service through Ajax, there really is no other way. (unless not relying on sessions at all is an option).
This is because of the way sessions are maintained: the ASP.NET runtime detects a session when the request contains a cookie. This cookie is set at every request / response and will contain an expiration date.
If in your initial request you set an expiration of 20 minutes and the user closes the browser or is inactive for more than 20 minutes there is no way on the server side you can detect which session the request belongs to.
So, to answer your question whether you can cancel the session_end, no you cannot do that as that code runs server side and it cannot access the client cookie. It's a simple event that is fired twenty minutes after you last set the cookie. This is completely asynchronous from the client-side.
The solution I proposed is a workaround that could work if you know how to calculate the duration (at least approximately).
Another solution, but way more complicated, would be to save the reports and make a separate section for the user where he can see all his reports. That way, even if the session times out, he can log back in and go to his history and retrieve the report.
It's best not to rely on Session_end as it doesn't always fire, such as when the worker process recycles or an uncaught exception occurs, i.e. it is basically killed / bounced.
If you want to revive a session then it seems that the best way is to store the user data somehow and totally manage the cache yourself.
It seems from your reply to previous posts that the additional network activity and subsequent page time load increase when using sql state management are unacceptable, and the difference between using sql server state provider to using a session server such as Microsoft AppFabric would be negligible, however it seems a distinct possibility that if you were to use the session server of AppFabric coupled with it's caching, things could be sped up a lot.
P.S. In general doing away with sessions would seem like the most efficient solution, see John Han's answer in this post just about sums it up sessions are bad mmkay.
In order for a session to stay alive, something (not necessarily the user's browser) has to make a request to your app with that user's ASP.NET_SessionId cookie every so often.
What if you had some code that saves the ASP.NET_SessionIds of the users you are interested in, and then have a windows service that requests a page on your app with the required ASP.NET_SessionId(s) every 20 minutes or so.
see http://erlend.oftedal.no/blog/?blogid=41 for some info about this cookie
Are you using FormsAuthentication? If so, you could increase the timeout for the authentication ticket, which will prevent the login screen even after the session has expired.
At the beginning of the request you could check the user through the ticket
After get the user if the session is null it means the user has been offline for while, you the can check the work in progress for that user.
If the user has a work in progress, load session values that you might need it and redirect them to the work in progress or report to download.
If the user has nothing, expire the ticket and redirect them to login page or just keep them logged in and reload session values.
The timeout for the authentication ticket is pretty big
http://msdn.microsoft.com/en-us/library/system.web.configuration.formsauthenticationconfiguration.timeout.aspx
Cheers
I'd suggest increasing the session timeout instead of trying to find a way around it. There's an interesting thread about session and form timeouts here
I suggest that you don't depend on sessions at all .. you can depend on the query string by adding a new GUID variable and use that variable value with the application object to map the user requested file(s) with the GUID value .. this way .. the user will always be able to download the file since he have the link to the file that is mapped to the application object and no need to handle any session timeout.
Why do not you try to show the loading file progress bar and inside that one you can use the logic of checking status of file downloaded so far. It will have two advantage , as you are hitting your website , Session will not expire at the same time you are giving the useful information back to end user.
Related
I have this piece of code:
var thisUser = Session["user"];
if (thisUser == null)
{
LogFile.Log("Logging out");
Response.Write("xp");
}
I am trying to track down why sometimes when I play with the system for a few minutes and suddenly the user session variable gets null.
It happens randomly in different scenarios.
I do not set the Session["user"] to null at any point.
Session timeout is set to 20 minutes.
I do not call Session.Clear() at any point.
Any ideas\thoughts\things I should look at as to why is it may happening?
I am using Firefox if that to any help.
The system is built with asp.net.
For more info please ask.
are you calling the same host? if the base URL is different the server will treat this as different users. for example:
http://localhost/path/to/resource and http://localhost:80/path/to/resource
both point to the same resource, but the requests are different and the session cookie will be different, or not present.
An easy way to test this is to launch your browser's developer toolbar and monitor the network traffic. compare the URLs to make sure they are the same base path and the same session cookie is passed in the request.
First of all this looks like C# and ASP.NET, not classic ASP. Now if you never clear the session yourself and the server (or the app pool) is never restarted, then the only way to lose the session is to clear the browser's cookies.
Editing the web.config will recycle the app pool, which clears the session info.
I set sessiontimeout in web.config file like following.
<sessionState timeout="120"
cookieless="false"
mode="InProc"
/>
Some times above code working fine.But many times session expired after 10 to 15 minutes automatically.
and this errors exists in every browser.
when i run same application using .net on my local computer it working fine.
Please note that, i haven't create global.ascx file yet.
Sessions will also be reset when the application recycles. This will happen when you change some things in the site, like change anything in the "bin" folder or modify the "web.config" file.
Also IIS may stop or recycle applications that it thinks are not used at the moment.
Are you sure you want to keep everything in memory for two hours, even after the user left? Better ways to keep the session alive would be to use some regular (every few minutes) AJAX callback. Search for "session keep alive asp.net ajax".
Have you verified the session timeout the app is ending up with? Do a response.write of Session.Timeout. This value can be set in several places.
How do you know the user is being active? The session will time out if he's only typing text, scrolling, and/or reading for lengths of time greater than the timeout value. The browser must invoke a HTTP request to the server to reset the timer.
As Hans mentioned, a background script-based "pinger" set to run at interval to access minimal web content will help.
Considering I'm about to start yet another web application, and I seem to need to add this functionality to most web apps, I was inspired to create a little package called Keep Me Alive to speed this process up in the future. You can find it here:
http://kma.codeplex.com/
Hope it helps!
I have an asp.net web-forms application which is using entity model. When a users logs in, we create a row in user_session table with timein. and when user logs out we update that row and put timeout.
Now problem is, when user closes the browser without logging out, how can we update the row in user_session table?
You can't. There is no way of the server being notified that a browser has been closed by the client. The best you could do is to schedule some job on your SQL Server which runs and updates rows. You may try also subscribing for the Session_End event in Global.asax but be careful because this event might never be triggered if you are using an out-of-process session.
Short answer is: You can't. That's why you shouldn't try to do such a silly thing.
The longer answer is: You have to put up with the delayed response of a session timeout. That could be a significant amount of time. In Global.asax there is an event called Session_End, which you can hook to do what you want.. but it won't show when the user closed their browser. It will only fire when the session has ended, which by default is 20 minutes after the last request. And that's only if it's an in-memory session and the process hasn't crashed.
The better solution is to just run a job every so often to clear the users online table. That solves the problem regardless, but requires more work for your job to figure out what to remove.
In your Global.asax.cs, you can code that in the Session_End event. It will not happen till the session timeout (default 20 minutes), but it will happen. You will likely want to delete any existing records on Application_Start and/or Application_End events (the Application_End event does not execute when the computer or web process crashes so best to use Application_Start) to make sure you start off fresh when the web application loads.
You can use the Event Session_End in the Global.asax.
There may be a delay between the browser closing and the actual session timeout on the server depending on your session settings.
I guess should be scheduled job that will close expired sessions like in AspState.
You can take a look into AspState database stored procedure DeleteExpiredSessions to see what you need to do. This procedure job with name ASPState_Job_DeleteExpiredSessions call each minute to close sessions.
Try the below code.
On the form_load event in every page, write the below code.
Response.AppendHeader("Refresh", "60; URL=../default.aspx")
Default session time out is 20 minutes and I am using "60" seconds for this demo. If there is no activity in this period of time, then it goes back to the default or home page and we send a key to update the database "session key" or any other tag. This way we can possibly know the activity of the user or member.
This worked for me.
I need to maintaing the Session throughout the day? On or before session expires, how do I enforce session throughout the day without giving any session timeout either in IIS or web.config?
Note: Default Session Timeout in IIS – 20 minutes.
Thanks in Advance
The first thing you can do is decouple the session from the process by using a SQL (or other database) session state server that holds the serialized copy of the session data attached to a session id. Then you could use a persistent cookie to pass the session id back and forth. Any session that expires could then be regenerated or reinstated.
Since you're wanting to maintain a session all day (which is innately insecure), we'll assume that most security considerations have already been thrown out. If you're concerned about replay attacks, you could use an HMAC to validate cookie session requests by having the session id, datetime, username (if that's part of your data), maybe IP address in a string followed by a one-way salted hash of this data. This way you rehash the data and compare the hash sent with the request with the generated hash from that data to see if the request is valid.
Forcing the page to be refreshed every 10 minutes or so by javascript. The session timeouts are how long after the last requested page, the session becomes invalid. When you refresh the page every 10 minutes or so, the session is extended for 20 minutes on every refresh.
If you refuse to change the setting in the web.config to a longer value, you could try to force the browser to refresh within a short time of the session timeout (eg at 17 minutes). There might be other nifty ways.
If you can find a way to do without the session, it will save you pain later. Either use a caching strategy (memcached) or look at your resource usage.
eg Do you really need to serialise X into the session?
Use StateServer instead of InProc
This delegates all session handling to a separate database and removes it from the servers process. This will mitigate problems with eating up server resources which then allows you to increase the session timeout to 24 hours.
I believe this is the only viable solution.
Peiter is on the right track -- making fake requests to keep things alive. You definitely don't want to refresh the page, that leads to unwanted updates and such. You don't even need to refresh part of the page. You just need to send an ajax request every X minutes while a user has a browser open to an IHttpHandler that does have session state enabled. This is very, very effective -- we've got one app which is compltely dependent upon sessions for everything that has a 5 minute session timeout. And a session heartbeat that fires every 4.5 minutes. Its been in production upwards of 3 years and we have not lost a session yet. And yes, session state is stored in process in this case -- the use of sessions is so heavy we don't want to wait for them to go out to SQL and back.
PS: protip -- make sure to make the request and response unique; caching kicks in otherwise in certain browsers rendering this trick ineffective.
put a ajax call into a timer, to a method which does nothing "", that's how even the user is on the page but not doing anything means "totally inactive", would not log out, we have implemented the same logic and resolved our problem, our target was 3 to 4 hours, and we did not want to change the value in web.config or using any other way cause of some reasons.
that ajax call would be in a "timer", set the timer time to "any".
Are we "doing it wrong"?
A colleague and I are messing around with an ASP.NET page to act as a "portal" to view the results from a diagnostic program on a UniData server. Although we do the odd-job of ASP/ASP.NET at work, it is not our primary language.
To access this server, we have to use UniObjects, which is an API for authenticating and using the UniData server.
We needed each user visiting the website to have to authenticate with UniData and get their own session via the UniObjects library, then be able to use it without signing in again (unless the session isn't used with in 'x' minutes).
The method we have come up with is as follows:
We have a singleton with a Hashtable. It maps Windows username with a session object.
If the user goes to our page and 'username' doesn't exist in the Hashtable, it redirects to a login page where the session object is created and added to the Hashtable if authentication succeeds. Otherwise, it grabs the users existing session object from the Hashtable and uses that for the request (unless it has expired, in which case we remove it and redirect to the login page).
Each session object (which is a wrapper object for stuff from UniObjects) has a "lastUsed" method. We need to clean-up user's sessions since we have license restrictions on users logged into the UniData server, so every time a user gets redirected to the sign-in page, it checks if any sessions have not been used in 'x' mins, in which case it closes that session and removes it from the Hashtable. It is done here so users won't experience any delay related to checking all sessions on every request, only at login.
Something is telling me that this solution smells, but I don't have enough ASP.NET experience to work out what we should be doing? Is there a better method for doing this or is it actually okay?
Since all of your users seem to be authenticated, I would suggest you think about using a different way of managing session state and timeout.
Part of the issue you have is that if a user just closes the browser without logging out, or stops using the application, you have to wait until the session times out to kill it off and free up UniObjects for your licensing issues.
My suggestion is as follows:
Add an invisible IFRAME to your
MasterPage template, or to each page
in the site if you aren't using
MasterPages.
That MasterPage will
load a KeepAlive.aspx page, that
contains a META Refresh, reloading
the page every 5 minutes.
You can
reduce the session timeout to 10
minutes (maybe even 6)
Now, if a user closes their browser windows, their session times out much quicker than usual, but if their browser window is left open, their session is persistent.
A code example and walkthrough can be seen here.
You now need to solution to prevent the user from leaving their browser window open all night and hogging your UniData licences. In this case I would implement a similar methodology, where a stagnant page (i.e. user has done nothing for 20 minutes) is refreshed to a logout ASPX page, clearing the session.
If you are using UniObjects COM, make sure you get your COM marshalling working correctly. Take a look at:
SafeCOMWrapper - Managed Disposable Strongly Typed safe wrapper to late bound COM
http://www.codeproject.com/KB/COM/safecomwrapper.aspx
Another thing to watch out for is that the dynamic array class in UniObjects COM has a threading issue that doesn't play nice with .NET. If you can, use your own dynamic array class or array splits in .NET instead of the Dynamic array class in UniObjects COM.
Sometimes when you try to access the data from the class, it shows an empty string, but when you debug it, the data is there. Don't know the root cause of this.
If you need a generic dynamic array class that works with .NET, I can supply you one.
UniObjects.NET does not have these problems to my knowledge.
Nathan Rector
When you say you are using UniObjects... are you using the COM or .NET object set? The easiest would be to use UniObject Conneciton pooling.
When you create your Singleton, are you storing it in the Application object, Session Object, or Cache Object?
I would suggest Application object, as the Session object is can do strange things. One way to handle and check timeouts would be to use the Cache Key with a CacheRemoveCallback. This way you can use a File/Path Monitor dependency to watch for a windows file change to cause a remove manually, or a timeout from the Cache dependency.
Draw back to this is that timeouts on Cache Dependencies are only driven by page activity, and if the asp.net session recycles, it may/will destory the cache dependencies.
Nathan Rector