I want to insert a visitor counter in my ASP site, so I've used a global.asax file to implement that. The problem is when the session ends the "AllVisitorCount" gets the default value which is set to 0 in my web.Config.
The code is:
void Session_Start(object sender, EventArgs e) {
// Code that runs when a new session is started
int allVisitorCount = 0;
if (Application["AllVisitorCount"] != null)
allVisitorCount = (int)(Application["AllVisitorCount"]);
else
Application.Add("AllVisitorCount", 0);
allVisitorCount++;
Application["AllVisitorCount"] = allVisitorCount;
}
Be sure you have slidingExpiration set to False in your web.config
"Application" is not a permanent object. It is created once your application starts (e.g the first session is started) and disposes of after your application pool times out. You can either persist your variable or simply change "Idle time-out" parameter in your AppPool (IIS=>Application Pools => your AppPool (or DefaultAppPool if you haven't defined one)=> Advanced Settings => Idle Time-out).
It's possible that your application pool is timing out with your session, thus clearing all Application variables.
Please provide more details such as: is this hosted in IIS, IIS Express, Cassini; how does the Session end (programmatically, timeout, etc.); have you placed a breakpoint on Application_End to see if the event is triggered.
Related
My session variables are being lost between pages. Interestingly, this seems to be environment-specific - in our production environment, this works fine, in our test environment, we lose the session variables. This previously used to work in our test environment with the same code, which leads me to believe it's some IIS or server setting that's different.
This is an integration to SFDC where I am adding some session variables on page load. Then, after a user goes through a login flow, SFDC calls back and I try to read those session variables.
Here's how I set the session variables:
Session.Add("tenantID", tenantId);
Session.Add("clientID", tenantInfo.SalesforceKey);
Session.Add("session", session);
Session.Add("clientSecret", tenantInfo.SalesforceSecret);
Session.Add("userEmail", user.Email);
Logger.Debug("Set session tenantID to " + int)Session["tenantID"]).ToString()); // This outputs the proper value.
However, in our callback function in the same controller, when running this code, all session variables are null.
public ViewResult Callback(string code)
{
Logger.Debug("Entering callback, code:" + code);
Logger.Debug("Session vars:");
if (Session["tenantID"] == null) // This is true
Logger.Debug("tenantID: null");
if (Session["clientID"] == null) // This is true
Logger.Debug("clientID: null");
if (Session["session"] == null) // This is true
Logger.Debug("session: null");
if (Session["clientSecret"] == null) // This is true
Logger.Debug("clientSecret: null");
// etc...
}
Initially I thought session was being ended, so I added the following in Global.asax. There's no session ended log line output until well after the callback executes.
void Session_End(Object sender, EventArgs E)
{
// Clean up session resources
Logger.Info("session ended for " + (string)Session["userEmail"]);
}
void Session_Start(Object sender, EventArgs E)
{
// Clean up session resources
Logger.Info("session started.");
}
Some clues that might help here:
- I ran a fiddler to capture the initial page load and the callback, and the ASP.NET session ID was the same in both requests:
(Page Load): Cookie: ASP.NET_SessionId=j1ggxuamkc2rk3q03z2vwye1
(Callback): Cookie: ASP.NET_SessionId=j1ggxuamkc2rk3q03z2vwye1
Previously, our logger statements would output to one file, however, we use log4net, and it now seems to be creating a second file to output the callback logger statements. In production, we only see one file. If I get a session end log from the Global.asax code in the first file (associated with page load), I can read the session values. If I get a session end log in the second log file (associated with callback), the session values are null again.
My web.config does not have any sessionState element included, and this is set the same across production and test.
Thank you for your help.
Here are the IIS Session State settings for that web application:
Session State Mode settings: Set to In Process
Cookie Settings: Mode: Use Cookies
Name: ASP.NET_SessionId
Time-out: 20 minutes
Regenerate expired session ID is unchecked
Use hosting identity for impersonation is checked
Compare the settings for the application pools between the test and prod environment.
I have also similar situation where our application runs fine in server and not in local due to session variable null. If you are implementing sessionfilter in MVC, it is better to run the application in IIS Express in visual studio instead of visual studio development server. That solves our problem of session loss.
In my Web Application, i am getting an error. "Session state has created session ID. But It can not save it because it was already flushed by application".
I googled for this issue and found that i have to store session id in Global.asax Session_Start Event.
string id = Session.SessionID;
But it was already exist in my application. I am not sure what else is causing issue. I was not using Response.Flush() also.
Can anyone please explain about this issue & fix for it.
That happens because sometimes (depending on the web.config configuration) the SessionID is not set in the cookie when Session_Start event executes in the global asax.
You encounter this error because at somepoint in the pagelifecycle a variable is set in the session. After the request ends, ASP.NET tries to set the SessionID too, but if the Request was flused (eg. this can be done by Response.Write or AJAX itself flushes the response) this exception will be thrown.
A simple fix would be (in the global.asax file):
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
//Ensure SessionID in order to prevent the folloing exception
//when the Application Pool Recycles
//[HttpException]: Session state has created a session id, but cannot
// save it because the response was already flushed by
string sessionId = Session.SessionID;
}
I have a site that is using Forms Auth. The client does not want the site session to expire at all for users. In the login page codebehind, the following code is used:
// user passed validation
FormsAuthentication.Initialize();
// grab the user's roles out of the database
String strRole = AssignRoles(UserName.Text);
// creates forms auth ticket with expiration date of 100 years from now and make it persistent
FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1,
UserName.Text, DateTime.Now,
DateTime.Now.AddYears(100), true, strRole,
FormsAuthentication.FormsCookiePath);
// create a cookie and throw the ticket in there, set expiration date to 100 years from now
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
FormsAuthentication.Encrypt(fat)) { Expires = DateTime.Now.AddYears(100) };
// add the cookie to the response queue
Response.Cookies.Add(cookie);
Response.Redirect(FormsAuthentication.GetRedirectUrl(UserName.Text, false));
The web.config file auth section looks like this:
<authentication mode="Forms">
<forms name="APLOnlineCompliance" loginUrl="~/Login.aspx" defaultUrl="~/Course/CourseViewer.aspx" />
</authentication>
When I log into the site I do see the cookie correctly being sent to the browser and passed back up:
HttpFox output http://cid-e79f8e4b07c3e30f.office.live.com/embedphoto.aspx/Public/SessionProblem.png
However, when I walk away for 20 minutes or so, come back and try to do anything on the site, the login window reappears. This solution was working for a while on our servers - now it's back. The problem doesn't occur on my local dev box running Cassini in VS2008.
Any ideas on how to fix this?
Session timeout and Forms Authentication timeout are two separate things. Is the Session timeout set to 20 minutes, and would it be logging your users out in the Session_End event in Global.asax file by any chance?
By default, app pools in IIS 6 are set to shut down after 20 minutes of inactivity. If there's nothing in your app configuration that's causing your app to shut down that quickly, check the app pool configuration in the IIS Manager. There are lots of wonderful knobs you can set in there.
Well I do have the following in Global.asax:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
//Fires upon attempting to authenticate the use
if (!(HttpContext.Current.User == null))
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity.GetType() == typeof(FormsIdentity))
{
FormsIdentity fi = (FormsIdentity) HttpContext.Current.User.Identity;
FormsAuthenticationTicket fat = fi.Ticket;
String[] astrRoles = fat.UserData.Split('|');
HttpContext.Current.User = new GenericPrincipal(fi, astrRoles);
}
}
}
}
Is that what you're referring to? Also, we're in an IIS6 environment if that makes any difference.
Another quick thing to check might be your hosting type. Cloud hosting will generally have a load balancer that is hard set to keep the same IP pointed to a node server for ~20mins, however after this time you might be pushed to a new server creating a new session on the new server and 'logging you out'
If your on standard shared hosting or a single dedicated server or virtual server however this won't be the problem :)
To get around this and keep the asp.net sessions working you need to move session state to a database - or re-tool your code to not use sessions at all :)
You might want to check whether you are using a load balancer. If so, then really you shouldn't be storing InProc. Should be looking into a state server or sql server if you have more than one entity.
Based on the issue, it seems that the default of 30 minutes isn't being adhered to either, which generally points to IIS/Hosting/Network configuration.
Is there a way to determine the number of users that have active sessions in an ASP.NET application? I have an admin/tools page in a particular application, and I would like to display info regarding all open sessions, such as the number of sessions, and perhaps the requesting machines' addresses, or other credential information for each user.
In global.aspx
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
Application["OnlineUsers"] = 0;
}
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
Application.Lock();
Application["OnlineUsers"] = (int)Application["OnlineUsers"] + 1;
Application.UnLock();
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate
// mode is set to InProc in the Web.config file.
// If session mode is set to StateServer or SQLServer,
// the event is not raised.
Application.Lock();
Application["OnlineUsers"] = (int)Application["OnlineUsers"] - 1;
Application.UnLock();
}
Note: The Application.Lock and Application.Unlock methods are used to prevent multiple threads from changing this variable at the same time.
In Web.config
Verify that the SessionState is "InProc" for this to work
<system.web>
<sessionState mode="InProc" cookieless="false" timeout="20" />
</system.web>
In your .aspx file
Visitors online: <%= Application["OnlineUsers"].ToString() %>
Note: Code was originally copied from http://www.aspdotnetfaq.com/Faq/How-to-show-number-of-online-users-visitors-for-ASP-NET-website.aspx (link no longer active)
ASP.NET Performance Counters like State Server Sessions Active (The number of active user sessions) should help you out. Then you can just read and display the performance counters from your admin page..
If you are using .net Membership you could use
Membership.GetNumberOfUsersOnline()
More about it: http://msdn.microsoft.com/en-us/library/system.web.security.membership.getnumberofusersonline.aspx
If you'd like to implement the same mechanism by yourself, you can define like a CurrentUserManager class and implement the singleton pattern here. This singleton object of class CurrentUserManager would be unique in the AppDomain. In this class you will create its self instance once, and you will prohibit the others from creating new instances of this class by hiding its constructor. Whenever a request comes to this object, that single instance will give the response. So, if you implement a list that keeps the records of every user (when a user comes in, you add him to the list; when he goes out, you remove him from the list). And lastly, if you want the current user count you could just ask the list count to this singleton object.
if you use sql server as the session state provider you can use this code to count the number of online users:
SELECT Count(*) As Onlines FROM ASPStateTempSessions WHERE Expires>getutcdate()
The way I've seen this done in the past is adding extra code to the Session_OnStart event in the Global.asax file to store the information in a session agnostic way, e.g. a database or the HttpApplicationState object. Depending upon your needs you could also use Session_OnEnd to remove this information.
You may want to initialise and clean up some of this information using the Application_Start and Application_End events.
The administration page can then read this information and display statistics etc.
This is explained in more depth at http://msdn.microsoft.com/en-us/library/ms178594.aspx and http://msdn.microsoft.com/en-us/library/ms178581.aspx.
You can use PerformanceCounter to get data from System.Diagnostics namespace. It allows you to get "Sessions Active" and much more. It allows you to get from local server as well as remote.
Here is an example of how to do it on local machine
void Main()
{
var pc = new PerformanceCounter("ASP.NET Applications", "Sessions Active", "__Total__");
Console.WriteLine(pc.NextValue());
}
or for remote server you would do:
void Main()
{
var pc = new PerformanceCounter("ASP.NET Applications", "Sessions Active", "__Total__", "ServerHostName.domain");
Console.WriteLine(pc.NextValue());
}
Performance Counters for ASP.NET provides full list of ASP.NET counters that you can monitor
Google Analytics comes with an API that can be implemented on your ASP.NET MVC Application.
It has RealTime functionality so the current amount of users on your website can be tracked and returned to your application.
Here's some information
We would like to have the FormsCookieName of FormsCookiePath change per instance of our application. We have an application which has multiple instances on 1 server/domainname. Because of this we can only work in 1 application at the same time, since the cookies will overwrite eachother. Same for the Sessions btw.
Is there a way to dynamicly, for example in the Global.asax Application_Start, change this name? This would be usefull as we keep a license name in each application which could be used as the basis for the CookieName.
We already work with Web.config and extra files to overwrite Web.config values in external files using: <appSettings file="Web.AppSettings.Config">
But this requires manual actions which can be forgotten and are redundant since the settings can be retrieved from the database.
Thanks.
I had similar situation, I did the following. In the Application_Start, I checked to see if my cookie name needed change. This would occur after a new deployment for all applications where I have the same web.config for all.
protected void Application_Start(object sender, EventArgs e)
{
// determine unique cookie name per application
string cookieName = ...
// Get the web.config forms settings
Configuration c = WebConfigurationManager.OpenWebConfiguration("~");
AuthenticationSection auth = c.GetSection("system.web/authentication")
as AuthenticationSection;
// See if we have mismatch in web.config or in Forms cookiename
if (auth != null && auth.Forms != null &&
(auth.Forms.Name != cookieName
|| FormsAuthentication.FormsCookieName != cookieName
)
)
{
// Assign value in web.config for future restarts
auth.Forms.Name = cookieName;
// would be nice if this restarted the app, but it doesn't appear to
c.Save();
// This seems to restart the app
System.Web.HttpRuntime.UnloadAppDomain();
}
...
}
The web.config is modified on the application start and then the web app is restarted. Next time the web app comes up, cookie names are in sync and the reset code is skipped.
I have been struggling with Cookies with quite a few days. It has been an awesome learning experience.
So wanted to share the possible ways I found & discovered: There are several HACKs to modify Forms Authentication Cookie name:
You can automate the modification of cookie name under Authenticaiton secion of Web.Config file in Application_Start event in Global.asax. Thanks to Ron for sharing this. But I could not guarantee that the user whose identity would be used to run application domain have enough privileges to modify the file on disk or not. Hence I needed an improvised solution, so I devised following.
Thanks to ILSpy for letting me see inside the FormsAuthentication class, and many thanks to Reflection to let me modify the private field of a class. I used following code to modify the cookie name on run-time with following small piece of code and this worked like a charm !!!
protected void Application_Start(Object sender, EventArgs e)
{
// This will enforce that FormsAuthentication class is loaded from configuration settings for the application.
FormsAuthentication.Initialize();
// The new cookie name whatever you need can go here, I needed some value from my application setting to be prefixed so I used it.
string newCookieName = string.Format("{0}.ASPXAUTH", ConfigurationManager.AppSettings["SomeSettingThatIsUniquetoSite"]);
// Modifying underlying baking field that points to FormsAuthentication.FormsCookieName
Type type = typeof(FormsAuthentication);
System.Reflection.FieldInfo field = type.GetField("_FormsName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
field.SetValue(null, newCookieName);
}
Suggestions, loopholes are requested as this is my first answer on this forum.
According to MSDN, the FormsAuthentication.FormsCookieName property that stores the cookie name is a read-only property. This property must be read from the web.config.
Each instance will need a separate name in the web.config. I suggest including the name of the authentication cookie in your existing change management system.