Are you using the new TransferRequest?
If so, then read on because you might get caught by some issues associated with the ASP.NET session state. If you haven't heard about this new cool method, then you don't know what you're loosing. The first thing you should know before hand is that you will only be able to use the method if you're hosting your pages on IIS 7 (the method won't work with previous versions of IIS).
ok, so what does this method do? well, for the page programmer, it's really similar to the old Transfer method, but better. When you issued a Server.Transfer, you're basically creating a new instance of the default page destination and executing it. It's really fast, but there were some problems with it. For instance, if you were writing module, there was simply no way to get a reference to that new page since that page would be instantiated "in-place" in the curren request. That's why Paulo create the cool Page Module concept. Even though that concept would let you get a reference to the page, you'd still have to be very carefull because the new page request wouldn't go through the ASP.NET pipeline (like what happens when you perform a normal request for a page).
Well, I'm glad to see that this no longer happens with the new TransferRequest method. Internally, it'll perform a complete request by using a IIS 7 worker thread. Basically, this means that the page request will go through the complete ASP.NET pipeline, giving every module a chance of interacting with the request. For instance, this means that authorization will be applied to the new url you're asking (which to me is something really cool :) ). Another great thing: you can redirect to any handler (which really didn't happen with the old Transfer method). And it seems like you can even define the headers that will be passed to the new handler you're asking for since one of the overloads of the method expects NameValueCollection parameter (ok, i really didn't test this :)).
Ok, now we know it's cool. but does it have any problems? well, yes, it has one big gotcha: if the parent request (the one from which you call TransferRequest) has acquired session state, then you need to release it before calling the method. If you don't, then the new request will block for several seconds. Do notice that this will affect mostly pages since the problem won't appear if you call the method before the AcquireRequestState or after the ReleaseRequestState HttpApplication events.
Here's why: if the parent request has acquired session state and calls TransferRequest, it'll wait until the child request finishes in order to execute the end request notifications. The end request notifications is the place where the request releases the session state (if it has acquired it). Well, it just happens that when the child request starts executing, it tries to get the session state (again, only if you're using it) which hasn't been released by the parent request. so, we're in a dead lock scenario (to be precise, this is not really a dead lock scenario because there's a timeout which will eventually be responsible for freeing the session and letting the child request go ahead and use it).
Ok, there's a workaround for this, but it's a little messy. The idea is to make sure that the parent request will release the session before starting the child request. To achieve this, you can call the CompleteRequest method and then, from within a method that handles the EndRequest event raised by the HttpApplication, you should perform the TransferRequest method call. Here's a global.asax file that shows how to achieve this easily:
<%@ classname="MyApp" %>
<script language="C#" runat="server">
string _transferRequestPath;
public void TransferRequest(string path) {
// remember the path for later
_transferRequestPath = path;
// short circuit the pipeline by jumping to the
// end request notifications where we can release
// session state
this.CompleteRequest();
}
// In Session_Start, we acquire session state. This will
// cause TransferRequest to hang unless we release session state first.
void Session_Start() {
}
// By the time Application_EndRequest is called, session state has been released
void Application_EndRequest() {
// we may need to call TransferRequest
if (_transferRequestPath != null) {
// make copy of path and set instance field to null
// since application instances are pooled and reused.
string path = _transferRequestPath;
_transferRequestPath = null;
Context.Server.TransferRequest(path);
}
}
</script>
Special thanks go to Thomas Marquardt for helping me out with this crazy transfer bug that appeared on a simple app when I dropped an empty global.asax file*. Besides finding the bug in a record time, he also wrote a quick workaround for it (shown before). Another thanks to Mike Volodarsky for heping me understand the purpose of the several methods that let you transfer a request.
* guess what, the empty global.asax had an empty Session_Start method. When this happens, you'll end up getting a session ID and you'll end up acquiring the session, leading you to the problem I've just described