Tuesday, October 21, 2003 - Posts

Another Server.Tranfer problem.

There are lots many intricacies of .NET that most of us who work on it are unaware of. It is the obvious truth but time and time again, the CLR proves that to you by taxing your brain with weird results and behaviour and makes you think about your coding pattern again ! Recently, one of my team mates got stuck with a really weird Server.Transfer problem and it drove us crazy trying to even find out the problem. Irony it seems that the delicate problem has been encountered by so many people and everyone have been cribbing about it in many forums. I tried to replicate the problem with a simple Web App and guess what, within minutes, my computer was spitting out the same error message too !!!
 
Here's the situation :
 
Just use this code below ... It is a cooked up code but the funda is that whenever you have a Server.Transfer in a try catch block, after some kind of code, or rather preprocessing, then the Server.Transfer will throw up an exception “Thread was being aborted.” Well because we have a Catch that watches over these exceptions, eventhough the exception occurs after the transfer, the Catch block is executed unnecessarily and hence the actual code gets buggered ... This was one major pain in one of our projects recently and took us quite a while to sort it out !
 
Warning : Dont get messed up with such code. 
 
 
   public class WebForm1 : System.Web.UI.Page
   {
      protected System.Web.UI.WebControls.Button Button1;
      public static bool Diversion = false ;
 
      private void Button1_Click(object sender, System.EventArgs e)
      {
          try
          {
               if(Diversion)
               {
                  Diversion = false ;
                  Server.Transfer("Test1.aspx") ;
               }
               else
               {
                  Diversion = true ;
                  Server.Transfer("Test2.aspx") ;
               }
         }
         catch(Exception err)
         {
            Response.Write("Error Message : " + err.Message);
            if(!Diversion)
            {
                  Server.Transfer("Error.aspx") ;
                  Diversion = true ;
            }
         }
      }
 
      override protected void OnInit(EventArgs e)
      {
            this.Button1.Click += new System.EventHandler(this.Button1_Click);
            this.Load += new System.EventHandler(this.Page_Load);
 
            base.OnInit(e);
      }
   }
 
 
One thing to remember about SmartNavigation is how SmartNavigation works. It takes postbacks and renders all the changed items to a hidden IFRAME, then replaces all the innerHTML control elements on the page with the results. This is what gives the illusion of a page that doesn't "jump" when reloaded. Server.Transfer() executes the referred page in the current context and does not actually readdress to it. It just clears the output buffer and executes another page producing output in the same buffer. Watch out for such details which can be important while doing such operations !
 
The workaround for this is to put the Server.Tranfer outside the try-catch block ( which is the obvious answer isn't it ?! ) but oh well the elegant solution for that is to catch the System.Threading.ThreadAbortException and handle it appropriately !!
 
catch(ThreadAbortException e)
{
    throw e;
    // or ignore ;)
}

This problem occurs in the Server.Transfer method because the method internally calls Response.End because of which the thread which has been aborted can no longer serve the consequent execution commands and hence the CLR throws up the exception.

According to one of the MS KB articles, to work around this problem, use the Server.Execute method instead of Server.Transfer. This will solve the issue ! Well this works and solves the problem but another important fact has to be remembered here. The Server.Execute function accepts a URL parameter, stops execution of the current page (where the call to the function is made), and transfers the current environment to the new page. When that new page finishes execution, then control returns to the calling page just after where the Server.Execute was called. (Server.Transfer works the same way but however, upon completion of the execution of the new page, processing ends and Response.End is called for the current context !)

Well that's it for now about Page navigation. Still learning about this and will be glad to hear any other facts that i have missed out.

Happy coding.