Basics of ASP.NET Session State

Recently I was working with a customer about basic stuff related to move their application using InProc session State to SQL Server Session State. Before putting hands to work I sent them a Basic Article that I read, written for an MVP founder of eggedgecafe.com, named Peter Bromberg. I decide to copy part of the article here to have it at hand.

ASP.NET Session State can store strings, ints, DataSets, and custom classes. Session comes in three flavors: InProc, StateServer, and SQL Server session state.

  

Basic use of Session in ASP.NET (C#):
 
STORE:
 DataSet ds = GetDataSet(whatever parameters);
 Session["mydataset")=ds;
 
RETRIEVE:
 DataSet ds = (DataSet)Session["mydataset"];
 
Storage location
 
InProc - session kept as live objects in web server (w3wp.exe). Use "cookieless" configuration in web.config to "munge" the sessionId onto the URL (solves cookie/domain/path RFC problems too!)

StateServer - session serialized and stored in memory in a separate process (aspnet_state.exe). State Server can run on another machine

SQLServer - session serialized and stored in SQL server

Performance
 
InProc - Fastest, but the more session data, the more memory is consumed on the web server, and that can affect performance.

StateServer - When storing data of basic types (e.g. string, integer, etc), in one test environment it's 15% slower than InProc. However, the cost of serialization/deserialization can affect performance if you're storing lots of objects. You have to do performance testing for your own scenario.

SQLServer - When storing data of basic types (e.g. string, integer, etc), in one test environment it's 25% slower than InProc. Same warning about serialization as in StateServer.

Performance tips for Out-of-Proc (OOP) modes
 
If you're using OOP modes (State Server or SQL Server), one of your major cost is the serialization/deserialization of objects in your ASP.NET session state. ASP.NET performs the serialization/deserialization of certain "basic" types using an optimized internal method. "Basic" types include numeric types of all sizes (e.g. Int, Byte, Decimal, ... etc), String, DateTime, TimeSpan, Guid, IntPtr and UIntPtr.

If you have an ASP.NET session variable (e.g. an ArrayList object) that is not one of the "basic" types, ASP.NET will serialize/deserialize it using the BinaryFormatter, which is relatively slower.

For performance sake it is better to store all ASP.NET session state data using one of the "basic" types listed above. For example, if you want to store two things, Name and Address, in session state, you can either:
 
(a) store them using two String session variables, or
 (b) create a class with two String members, and store that class object in a session variable. Performance wise, you should go with option (a).

Robustness
 
InProc - Session state will be lost if the worker process (w3wp.exe) recycles, or if the appdomain restarts. It's because ASP.NET session state is stored in the memory space of an appdomain. For details, see KB324772.

StateServer - Solve the session state loss problem in InProc mode. Allows a webfarm to store ASP.NET session on a central server. Single point of failure at the State Server.

SQLServer - Similar to StateServer. Moreover, session state data can survive a SQL server restart, and you can also take advantage of SQL server failover cluster, after you've followed instructions in KB 311029.

Caveats
 
InProc
 
- It won't work in web garden mode, because in that mode multiple w3wp.exe will be running on the same machine. Switch to StateServer or SQLServer when using web garden. Also Session_End event is supported only in InProc mode.

StateServer

- In a web farm, make sure you have the same <machineKey> in all your web servers. See KB 313091 on how to do it.
- Also, make sure your objects are serializable. See KB 312112 for details.
- For session state to be maintained across different web servers in the web farm, the Application Path of the website (For example \LM\W3SVC\2) in the IIS Metabase should be identical in all the web servers in the web farm. See KB 325056 for details

SQLServer

- If you specify integrated security in the connection string (e.g. "trusted_connection=true", or "integrated security=sspi"), it won't work if you also turn on impersonation in asp.net. Unfortunately, this bug isn't reported in KB yet. (There is a QFE fix for it.)

- Also, make sure your objects are serializable. See KB 312112 for details.

- For session state to be maintained across different web servers in the web farm, the Application Path of the website (For example \LM\W3SVC\2) in the IIS Metabase should be identical in all the web servers in the web farm.   See KB 325056 for details.
 
FAQ's:
 
Q: ASP.NET Session states works on some web servers but not on others.
A: Maybe machine name problem. See https://support.microsoft.com/default.aspx?scid=kb;EN-US;q316112 .
 
Q: Why isn't Session_End fired when I call Session_Abandon?
A: First of all, Session_End event is supported only in InProc mode. In order for Session_End to be fired, your session state has to exist first. That means you have to store some data in the session state and has completed at least one request.

Q: Why are my ASP.NET Session variables lost frequently when using InProc mode?
A: Probably because of application recycle. See https://support.microsoft.com/default.aspx?scid=kb;en-us;Q316148

Q: Why does the SessionID remain the same after the Session times out or abandoned?
A:Even though the session state expires after the indicated timeout period, the session ID lasts as long as the browser session. What this implies is that the same session ID can represent multiple sessions over time where the instance of the browser remain the same.

Q: Why does the SessionID changes in every request?
A: This may happen if your application has never stored anything in the session state. In this case, a new session state (with a new ID) is created in every request, but is never saved because it contains nothing.

However, there are two exceptions to this same ASP.NET session ID behavior:

- If the user has used the same browser instance to request another page that uses the session state, you will get the same session ID every time. For details, see "Why does the SessionID remain the same after the Session times out?"
- If the Session_OnStart event is used, ASP.NET will save the session state even when it is empty.

Q: Can I share session state between ASP.NET and ASP pages?
A: Yes! Here is our article on how to do this in either direction using two "intermediate" pages. And here is an article on how to do it with SQL Server.  https://www.eggheadcafe.com/articles/20021207.asp
 
Q: What kinds of object can I store in session state?
A: It depends on which mode you are using:
- If you are using InProc mode, objects stored in session state are actually live objects, and so you can store whatever object you have created.
- If you are using State Server or SQL Server mode, objects in the session state will be serialized and deserialized when a request is processed. So make sure your objects are serializable and their classes must be marked as so. If not, the session state will not be saved successfully. In v1, there is a bug which makes the problem happen unnoticed. See this KB for more info:
https://support.microsoft.com/directory/article.asp?ID=KB;EN-US;q312112

Q: How come Response.Redirect and Server.Transfer is not working in Session_End?
 A: Session_End is fired internally by the server, based on an internal timer. Thus, there is no HttpRequest associted when that happens. That is why Response.Redirect or Server.Transferdoes not make sense and will not work.

Q: Do I have a valid HttpContext in Session_End?
A: No, because this event is not associated with any request.

Q: Will my session state be saved when my page hit an error?
A: No. Unless you call Server.ClearError in your exception handler.

Q: How do I use session state with web services?
A: The extra trick needed is on the caller side. You have to save and store the cookies used by the web service. See the MSDN documentation on HttpWebClientProtocol.CookieContainer property.   However, please note if you're using proxy object to call a web service from your page, the web service and your page cannot share the same session state due to architecture limitation.  This can be done if you call your web service through redirect.

Q: I am writing my own HttpHandler. Why is session state not working?
A: Your HttpHandler has to implement the "marker" interface IRequiresSessionState or IReadOnlySessionState in order to use session state.

Q: I am using a webfarm, and I lost session state when directed to some web servers.
A: For session state to be maintained across different web servers in the web farm, the Application Path of the website (For example \LM\W3SVC\2) in the IIS Metabase should be identical in all the web servers in the web farm.   See KB 325056 for details.

Q: Why isn't session state availabe in the Application_OnAcquireRequestState (or other) event handler?
A: Session state is available only after the HttpApplication.AcquireRequestState event is called. For details, see: https://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconhandlingpublicevents.asp

Q: If using "cookieless", how can I redirect from a HTTP page to an HTTPS page?
A: Try this:
String originalUrl = "/fxtest3/sub/foo2.aspx";
String modifiedUrl = "https://localhost" + Response.ApplyAppPathModifier(originalUrl);
Response.Redirect(modifiedUrl);

NOTE: Fully qualified URLs in the response.redirect, server.transfer, and FORM action
tags cannot be used with cookiless sessions. Here is an example of a fully qualified

URL: https://www.eggheadcafe.com/default.asp   More info here:
 
Q: What isn't Session available in my event handler in global.asax?
A: It depends on which event you're handling. Session is available only after AcquireRequestState event.

Q: Does session state have a locking mechanism that serialize the access to state?
Session state implements a reader/writer locking mechanism:
- A page (or frame) that has session state write access (e.g. <%@ Page EnableSessionState="True" %>) will hold a writer lock on the session until the request finishes.
- A page (or frame) that has session state read access (e.g. <%@ Page EnableSessionState="ReadOnly" %>) will hold a reader lock on the session until the request finishes.
- Reader lock will block a writer lock; Reader lock will NOT block reader lock; Writer lock will block all reader and writer lock.
- That's why if two frames both have session state write access, one frame has to wait for the other to finish first.