In order to understand why an output-cached request will not create a session, you need to understand a little bit about the ASP.NET Application Life Cycle, the events . The basics are that are a defined series of HttpApplication
events that may fire during each request. These events are typically subscribed to using event handlers by either HttpModule implementations or a Global.asax file.
Not every application event fires on every request, but the events that do fire will always fire in the defined order. The order of events that fire on IIS 7
1. BeginRequest
2. AuthenticateRequest
PostAuthenticateRequest
3. AuthorizeRequest
PostAuthorizeRequest
4. ResolveRequestCache
PostResolveRequestCache
5. MapRequestHandler (Integrated Mode Only)
PostMapRequestHandler
6. AcquireRequestState
PostAcquireRequestState
7. PreRequestHandlerExecute
<--- the IHttpHandler.ProcessRequest() method is called here
PostRequestHandlerExecute
8. ReleaseRequestState
PostReleaseRequestState
9. UpdateRequestCache
PostUpdateRequestCache
10. LogRequest (Integrated Mode Only)
PostLogRequest (Integrated Mode Only)
11. EndRequest
12. PreSendRequestHeaders
13. PreSendRequestContent
How an IHttpModule
Works
The IHttpModule interface looks like:
public interface IHttpModule
{
void Init(HttpApplication context);
void Dispose();
}
The Init()
method is used to subscribes event handlers to the application events and the Dispose()
method cleans up after the module when the application is done with it.
How ASP.NET Sessions Work
System.Web
defines an IHttpModule
implementation named System.Web.SessionState.SessionStateModule
. If the session is not disabled in the web.config, the following event handlers get wired up:
// app is the current HttpApplication ;
app.AddOnAcquireRequestStateAsync(this.BeginAcquireState, this.EndAcquireState);
app.ReleaseRequestState += this.OnReleaseState;
app.EndRequest += this.OnEndRequest;
Sessions work differently depending on what session mode is running but the key thing to understand is that sessions are retrieved and created in the SessionStateModule.BeginAcquireState
method and that method is wired up asynchronously to the AcquireRequestState
event.
How Output Caching Works
System.Web
defines an internal IHttpModule
implementation named System.Web.Caching.OutputCacheModule
. The Init()
method looks like:
void IHttpModule.Init(HttpApplication app)
{
if (RuntimeConfig.GetAppConfig().OutputCache.EnableOutputCache)
{
app.ResolveRequestCache += new EventHandler(this.OnEnter);
app.UpdateRequestCache += new EventHandler(this.OnLeave);
}
}
The OnEnter
method evaluates the cache parameters and looks for a cached response that matches the request. The OnLeave
method caches cacheable responses. The one thing you need to know is that if the OnEnter
method is successful in retrieving a cached response, it calls the CompleteRequest() method on the HttpApplication
instance. The documentation for this method reads: "Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event". So, any events that occur between the time that CompleteRequest()
is called and the EndRequest()
event are skipped.
Why is session not created if page is cached?
- The
SessionStateModule
subscribesSessionStateModule.BeginAcquireState
to the application'sAcquireRequestState
event, so that is where sessions are created and retrieved. - The
OutputCacheModule
subscribesOutputCacheModule.OnEnter
to the application'sResolveRequestCache
event, so that is where cached responses are retrieved and whereCompleteRequest()
gets called. That means that the following events will never fire for a cached request:MapRequestHandler/PostMapRequestHandler
;AcquireRequestState/PostAcquireRequestState
;PreRequestHandlerExecute
;IHttpHandler.ProcessRequest
;PostRequestHandlerExecute
;ReleaseRequestState/PostReleaseRequestState
;UpdateRequestCache/PostUpdateRequestCache
; and,LogRequest/PostLogRequest
. - If a cached response is retrieved, then
HttpApplication.CompleteRequest()
is called, theAcquireRequestState
event never fires, and the session is never created.
What to do about it?
Whether disabling output cache on pages in favor of controls is a viable solution depends on: (1) whether the code that uses the session is in the controls or in the pages; (2) how much load your application instance needs to be able to support; and, (3) what other caching mechanisms exist in either the application or its supporting infrastructure. Depending on your infrastructure, there may be other factors to consider. For example, ASP.NET sessions can perform and behave very differently depending on the session state mode and on whether the environment is load balanced or not. Also, if you happened to be running an HTTP accelerator cache like Varnish (for example), enabling sessions on a previously output-cached ASP.NET paged might change the behavior from omitting a session to attaching a stale session that belongs to a different user. This is just a hypothetical example, but the point is that there may be additional factors to consider when making these kinds of decisions.