Victor Garcia Aprea : Monday, June 16, 2003 - Posts

Subscriptions

<September 2010>
SuMoTuWeThFrSa
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

Post Categories




Monday, June 16, 2003 - Posts

The not so clear Error event

The abstract TemplateControl class defines an Error event which –by the docs– “…occurs when an unhandled exception is thrown…”. Currently there are only two subclasses of TemplateControl, they’re the famous Page and UserControl. Based on this you can easily infer that any unhandled exception will cause the Error event to fire and you would have a chance to do whatever you want. Up to here everything seems fine so let begin to complicate things a bit…

 

You already know that to catch an exception you need to explicitly write a try/catch block; but you are not writing one in your Page or UserControl-derived class and the Error event is still firing properly when an exception goes unhandled. So, where is the little magic to make this happen hidden?

 

We’ll begin by tracing the execution of a Page starting at its implementation of IHttpHandler.ProcessRequest. What interests us most about this method is that depending if transactions are supported or not on the page it will call ProcessRequestTransacted or ProcessRequestMain respectively.

 

ProcessRequestMain is actually the one in charge for directing the execution of our Page; it will initialize, load viewstate, process postdata, etc, for every control in our page and all this code will be enclosed in a try/catch block (can you see the light now?). A simplified pseudo-code of ProcessRequestMain would look like this:

 

try

{

     // For every child control:

     // Call Init

     // Call LoadViewState

     // Call ProcessPostData

     // Call Load

     // and the list goes on…
}

catch(ThreadAbortException)

{
     // Call Unload for every child control

}
catch(ConfigurationException)
{

     throw;
}
catch(Exception e)
{

     if(!HandlerError(e))
          throw;

}

 

 

Note that the code enclosed in the try block consists of what is usually referred as the control execution lifecycle. Following that, we have three catch blocks:

 

-          the first one is just there to ensure that Unload (and Dispose) get properly called even when a page is prematurely ending its execution (ThreadAbortException is a special type of exception that would actually deserve its own post).

-          the second one is there to… well I’m not sure. I believe the reasoning behind this may be that a ConfigurationException is more of an application-related exception than a page-related exception so it may make some sense for this exception not to be handled at the page level. Anyway, it should be good to properly document this behaviour in the docs.

 

Now, if none of the two previous catch blocks got executed, our code will –unfailingly– reach the third one. Inside this last catch block the private Page.HandlerError method is called and depending on its results, the caught exception may be re-thrown thus giving an application-level error handler a chance to catch it later on. The basic idea here is that you could do whatever you want plus having the chance to decide if you want your application-level error handler to still be able to “see” this exception later on.

 

Getting our event handler code called

 

It is Page.HandlerError method the one that will call TemplateControl.OnError which in turns will fire any delegates attached to the Error event (does this pattern sound familiar to you?). The code looks something like this:

 

private bool HandlerError(Exception e)

{

     // …
     Context.TempError = e;
     base.OnError(EventArgs.Empty);
     if(Context.TempError == null)

          return true;

     else
          return false;

     // …
}

 

TempError is an internal HttpContext property that wraps a private member variable that holds the exception that was thrown. If after calling OnError (which in turn will fire all attached delegates) TempError was not cleared (set to null), it means we want to let an application-level error handler “see” the exception. But being TempError an internal property how are we supposed to clear it in our event handler code? We can do this by calling the public HttpContext.ClearError method. Note that to make traditional ASP developers feel right at home, the ASP.NET team added an HttpServerUtility.ClearError method which is just a wrapper for HttpContext.ClearError, so you can write the more familiar:

 

Server.ClearError();

 

Instead of the not-so-popular-but-equally-effective:

 

Context.ClearError();

 

Error event don’t firing at all

 

We saw that any exceptions thrown by code enclosed in the try block in ProcessRequestMain will be caught by the catch block and the Error event will fire. This is true for example for any event handler we attach to a control’s event, any other method that these may call, etc. But what happens for code that is actually not enclosed by the above mentioned try block? Take for instance the instantiation of a declaratively added custom control that takes place in the class generated by the parser; that will not be enclosed by the try block shown previously thus any exception that the type constructor (or any other method called by it) may raise won’t be caught by the catch block and the Error event won’t fire. If the exception goes unhandled the application-level error handler still can “see” it, but this is not really what we want, after all it was an exception thrown by a control living on a page, it is an exception related to a page. Be well aware of this as the docs won’t help here.

 

What’s the story with User Controls?

 

All the explanation above seems to work well for Page but what happens if we attach a delegate to a UserControl’s Error event? If any exception thrown by code in our UserControl goes unhandled the Error event for the UserControl will not be called. Why? Simply because there is no try/catch logic coded anywhere to make this happen (like ProcessRequestMain does for Page). The documentation doesn’t state this properly which makes people think it can still be done.

 

Lastly, if you’re already wondering if the Error event at the application-level (HttpApplication.Error) works the same way as what we just saw, the short answer is: yes; the long answer would actually require its own post J

 

posted Monday, June 16, 2003 1:02 PM by vga with 108 Comments

Towards more processing at the client-side

Currently at my day-job, we’re architecting a framework to make ASP.NET a better ASP.NET. Basically, this fx is being used by developers to port just too many intranet/internet web applications to the .NET universe, for a just too big worldwide company. One of our key goals while architecting it  was to move as much processing to the client as possible; we did a lot of work in this regard, which involved providing a client-side framework and lots of enhancements at the server-side to actually take advantage of it.

Current versions of ASP.NET don’t really make the browser do much JavaScript stuff at all which translates to writing all your code at the server-side and having to cause a postback for almost anything you want to process. But life would be much better if -whenever possible- no postback occurred, or at least just a “soft” one (more on this later).

Do you think you really know JavaScript? Take a look at the inner workings of a project like Tibet, and think again. It would be really useful to count with some base library at the client-side to make interaction between the different controls and the page simpler (abstracting you from browser-specific related issues) and cut as much as possible the dependency on posting back to the server. Of course this would require developers to have some understanding of JavaScript beyond the alert function… but it would definitively pay in the long run.

Moreover, the Microsoft XMLHTTP component (or Netscape’s verbatim XMLHttpRequest) can really help in avoiding entire postbacks and ugly UI refreshes at the browser to happen (yes this is what I previously referred as a “soft” postback). Keep in mind that today every control that fires a postback, causes the entire form collection to be posted and lots of processing to happen at the server-side; two particular expensive things that are really not necessary all the time.

 

posted Monday, June 16, 2003 12:29 PM by vga with 3886 Comments