10

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?

Jesse
  • 8,605
  • 7
  • 47
  • 57
Ratna
  • 2,289
  • 3
  • 26
  • 50

10 Answers10

6

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

  1. 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.

  2. There are obvious performance/maintainability improvements that could be done (especially with having duplicate code in ResetItemTimeout and ResetItemTimeout2).

  3. 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.
Jesse
  • 8,605
  • 7
  • 47
  • 57
  • Jesse I tried to implement it, but it increased the load time considerably. – Ratna Apr 06 '13 at 11:30
  • Did you try using SQL as a backend, instead of MS Access? How about using `SqlConnection` instead of `OdbcConnection` (replace all Odbc with Sql)? – Jesse Apr 06 '13 at 11:35
  • our whole project is on ms sql so no need to use as acess. actually we implented this b4 also because we needed to pass session values betweem classic asp and asp.net. – Ratna Apr 06 '13 at 11:44
  • I'm referring to the Session-State Store Provider using SQL Server - the MSDN article uses MS Access, and therefore Odbc. If you use the .NET SQL Client + SQL Server, you should have better performance. I'm sing SQL Server as the Session-State Store Provider on one of my sites, and I did not notice much of a performance hit; **initiating** a session went from a few milliseconds to almost a second or two only. – Jesse Apr 06 '13 at 11:51
  • YES you are right, its takes max 2 sec on sql session, but there are many more overhead when you are doing it with a live site and performance is the first and last word. I have already demanded for vps but u know its hard for a developer when input cost is a concern for management. I had implemented this b4 but whole system just broke down. your solution can work when developed with this keeping in mind but its rearly hard when serialization and you dont know what is being is saved in session? – Ratna Apr 07 '13 at 10:12
  • Yup, I agree - costs are everything to management! What do you mean by the "whole system just broke down"? I don't think using this solution makes using the `Session` any harder, since it takes care of the serialization/deserialization. Is that what you meant? – Jesse Apr 07 '13 at 11:35
4
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

Pandian
  • 8,848
  • 2
  • 23
  • 33
  • My download page already has ajax timer which refreshes the page in 5 min. Howerver the admin people are in the habit of doing other things which changes the page location. – Ratna Apr 01 '13 at 11:46
4

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.

Kenneth
  • 28,294
  • 6
  • 61
  • 84
4

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?

Ratna
  • 2,289
  • 3
  • 26
  • 50
3

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.

Community
  • 1
  • 1
Paul Zahra
  • 9,522
  • 8
  • 54
  • 76
2

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

2

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

1

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

Community
  • 1
  • 1
Simon
  • 8,981
  • 2
  • 26
  • 32
  • 2
    Why should i increase the session timeout?? I just need them for 2 or 3 user that also in a week. Dont you think it will be a waste of resources?? when 99% of user dosent require long session. – Ratna Apr 01 '13 at 11:28
1

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.

Samir Adel
  • 2,691
  • 2
  • 15
  • 16
  • Dear samir, I dont feel that will be possible since i will have to change all authentication code. The problem is not just downloading the file. The admin sometimes merge 2 or more file before posting to amazon, for which i have to track all files created by him. – Ratna Apr 01 '13 at 12:06
  • I think you can use my technique only in the pages that you need the user to complete the action before he leave your system .. you can keep your authentication as it is now and you can use this technique for the download scenario only. – Samir Adel Apr 01 '13 at 12:32
1

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.

Devesh
  • 4,500
  • 1
  • 17
  • 28