LA.NET [EN]

Oct 09

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

7 comments so far

  1. Mike Volodarsky
    10:12 pm - 10-9-2007

    Good writeup, Luis. I”ll probably post more info about TransferRequest in the near future, as its not very well documented at all at this point.

    The most interesting area is TransferRequest as general replacement for RewritePath, as there are some very interesting scenarios where RewritePath does not give you what you want, even in the Integrated pipeline.

    Thanks,

    Mike

  2. Luis Abreu
    10:23 pm - 10-9-2007

    Hello Mike.

    yeah, that would be great. btw, one question about the iis.net site: can we trust its docs section?

  3. Israel Aece
    2:28 am - 10-10-2007

    Hello Luis,

    I believe that my test worked well because I hadn”t Global.asax file in my sample application.

  4. Luis Abreu
    8:10 am - 10-10-2007

    Hello Israel.

    Yes, you”re right…It took me several hours to be able to reproduce the bug. In fact, I had to start removing everything from my app until I stop getting the error. Unfortunately, the global.asax was the last thing I removed 🙂

  5. Mike Volodarsky
    5:54 pm - 10-11-2007

    Luis,

    The articles on IIS.NET are the best source of information on IIS7 so far, but some of them are outdated. Be mindful of the article update date and the tested with build to know how up to date the article is.

    Also be mindful of the differences between Vista and Windows Server 2008, which will be there until Vista SP1. Until RTM, the server release is also in flux so at times the product is ahead of the articles.

    I also try to cover special topics of interest on my blog, http://mvolo.com. If there are certain topics you”d liket to hear more about, let me know.

    Thanks,

    Mike

  6. ecards
    10:03 pm - 6-28-2008

    I get a ASP.Net page “hanging” just like you describe if I define the Session_Start event with no code.

    If I remove the even, the entire app works perfectly.

    This has been the worst problem switching over to Server 2008.

    I wonder if it”s related to your issue somehow, even though I”m not using TransferRequest?

  7. Christian
    11:33 am - 3-12-2010

    Hello, I used to use Server.Transfer, but I changed one application from IIS 6 to IIS7 and it stop working because it uses redirecting.

    Thanks to your post, I have resolved my problem use Server.TransferRequest.

    Nice!