Introduction
My last project was a banking application in which users were be able to access the application based on their login ID they use to login to the LAN. There could be situations in which the users may keep the application open for quite a long time. (Our staff never used to shut down their PCs even during power failures :D). During such a long period of inactivity, chances are high that the values of the session variables may get cleared. If the user continues with the application, unexpected, ugly exceptions could happen, which is not at all a good thing.
Problem
Prompting the user to login again and resetting the session variables was not an alternative for my application. Even though we can specify a huge session time out in the config file, (couldn't figure out the maximum value yet), it has got some limitations like the one discussed above.
Solution
On this site, I found something very useful in which the author was discussing a very beautiful idea for preventing session timeouts. (View it here.) The idea is to call a function similar to the following in the Page
's Load()
event.
private void ReconnectSession()
{
int int_MilliSecondsTimeOut = (this.Session.Timeout * 60000) - 30000;
string str_Script = @"
<script type='text/javascript'>
function Reconnect()
{
window.open('reconnect.aspx','reconnect','width=5,height=5,top=1800,
left=1800,scrollbars=no,showintaskbar=no');
}
window.setInterval('Reconnect()',"+int_MilliSecondsTimeOut.ToString()+ @");
</script>
";
this.Page.RegisterClientScriptBlock("Reconnect", str_Script);
}
Using the above code, the page will restore the session variables 30 seconds prior to the session timeout specified in the web.config file. What it does is call a JavaScript function that opens a page in a new window. This JavaScript function call is "scheduled" so that it will happen when the session is going to expire in 30 seconds. window.setInterval()
is used for that purpose.
In my application, some details were to be retrieved from the database at the time of session renewal. Reconnect.aspx is the page in which these things are done. You can put your own code in that page. In the original article on CodeProject, the author was trying to embed the page within a JavaScript Image()
tag. This was not suitable for my application. So, 30 seconds prior to session expiry, I am opening a window with the above properties, and after refreshing the session variables, it will close automatically. Since that idea is pretty straightforward, I am not discussing it here.
Tricky Part
The trickiest part of the game is yet to come. If the application consists of more than 50 or 100 pages, in order to avoid pasting the code in every page, we can make use of the inheritance feature of web forms. As we know, all web forms are inherited from the System.Web.UI.Page
class. We can add a class file to the project that inherits from the System.Web.UI.Page
class.
public class YourNewPageClass : System.Web.UI.Page
Then, add the following code to the class:
override protected void OnInit(EventArgs e)
{
base.OnInit(e);
if (Context.Session != null)
{
int int_MilliSecondsTimeOut = (this.Session.Timeout * 60000) - 30000;
string str_Script = @"
<script type='text/javascript'> function Reconnect()
{
window.open('reconnect.aspx','reconnect','width=5,height=5,top=1800,
left=1800,scrollbars=no,showintaskbar=no');
}
window.setInterval('Reconnect()',"+int_MilliSecondsTimeOut.ToString()+ @");
</script>";
this.Page.RegisterClientScriptBlock("Reconnect", str_Script);
}
}
By adding the above code, we override the OnInit()
event of the Page
class to include our session refreshing code. Calling base.OnInit(e)
is very important because we do not want to touch the normal behaviour of the Page
class. It's done! Now we have a new class that has all the properties and methods of a normal page class and whose session never expires. All you have to do is just inherit the pages of your application from the newly created class instead of from the default Sytem.Web.UI.Page
. The session variables in your application will never expire.
AJAX Approach
As per the comments by krafael_net and dapoussin, opening a popup window may not be applicable for all since there are plenty of popup blockers available and the latest browsers have the feature within themselves. Because my application will be used in our own network, problems in opening a new window are less.
Instead of using window.open()
in the JavaScript function Reconnect()
, we can get a little help from AJAX. As we know, AJAX scripts make a request to the server by using XMLHTTPRequest
s. Based on this idea, we can modify the JavaScript Reconnect()
function as below:
function Reconnect()
{
var url='reconnect.aspx'
xmlHttp=getObjectHTTP()
xmlHttp.open('GET', url , true)
xmlHttp.send(null)
}
With IE, the instance of XMLHTTPRequest
can be created like:
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP")
whereas in other browsers, we can instantiate it as:
xmlHttp = new XMLHttpRequest();
The getObjectHTTP()
function in the second line does that. Here is the code for this function:
function getObjectHTTP()
{
var xmlhttp = null
if (navigator.userAgent.indexOf("MSIE")>=0)
{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP")
}
else
{
xmlHttp = new XMLHttpRequest()
}
return xmlhttp
}
Done! In this AJAX approach, what happens is, on reaching the deadline of session expiry (we have set it as 30 seconds), the base page class from which we have inherited all the pages of the application forces to send a request to the server to refresh the session variables. This happens in the background.
Summary
There are many situations in which we don't want to lose session variables of our application. The above discussed idea is one of the simplest ways that we can follow to tackle the situation. Above all, we are making use of the real inheritance concept in our application. (Don't forget that Visual Studio itself creates web forms by inheriting from the Page
class ;)) Thanks to Ach1lles who gave me a chance to enhance the real idea. Hope this tip saves a lot of precious time for developers out there.