I have a large ASPX page with many ASCX controls. If a control throws an exception, it should log the exception and hide only itself. All the other controls should still render.
How do I handle exceptions on individual ASCX's raised from the front-end file (the ASCX and not the code-behind)? for example: a control trying to reference an invalid property using the <%= MethodThatThrowsANullReferenceException() %>
syntax.
Obviously using the generic error handler method in Global.asax won't solve the problem. I need to handle exceptions on individual controls.
Make all your UserControls inherit from a custom base class, like such:
I tried overriding Render method but this doesn't cover all exceptions.
For example, if some kind of exception is thrown during Page_Init, Load or Render, this will prevent the page from rendering.
We have different people working on different modules (controls) that can be loaded into a single Page, but I'm not responsible for the quality of the code of each module, so even if it's not best practice, i needed to catch exceptions and identify which control fails to load, because the application can't fail just because one module does.
For this particular scenario that is not so rare nowadays, neither custom, application or page error handling will work well.
The solution I've come up was:
Each Module (Control.ascx) when needs to be loaded into the Page (aspx) , is contained into a ModuleShell that will hold some specific features and will be responsible for helping the Page_Error handling to work properly.
This ModuleShell , instead of trying to trap the exception of its child control that failed, will just monitor in each life cycle stage if it managed to Load properly.
Here's an snippet of it:
Modules is a static class used to store session variables. CurrentState is a variable that ModuleShell use to record their names in.
The Page_Error located in the only aspx we got, will get the last recorded ModuleShell that tried to load. Since any exception will stop page rendering, the last ModuleShell to record its name to the main Page, it's probably the one that failed to load properly.
It's a sloppy solution but it's transparent to the Module Developer.
You could wrap the method you are trying to call in your own method that will return the same type, but with a
try {} catch {}
block.AFAIK, this is not possible (at least in an easy way).
Rich Custom Error Handling with ASP.NET:
If there is an exception occured inside the user control, the only way to catch it inside the user control is to handle it inside a
try { } catch { }
block.I think the lowest level when the exception like this could be caught is the next -
Page_Error
level like this:the
Context.ClearError()
method is even preventing an exception from bubbling up further on toApplication_Error
. But unfortunatelly, then unhandled exception is thrown the page processing stops and error processing is started instead. This means the render of page will stop too (so you won't see the controls next to that which caused this exception).One option, as suggested by Jim Bolla was to make all controls inherit from the same base class and use a Try/Catch in the Render method. This would have worked. Unfortunately many of the controls I am dealing with already have different base classes.
This solution worked for me:
I added the following code to each user control (I'm sure this can be refactored further to reduce duplication):
This catches any front-end rendering problems. The parent page can handle the ControlCrashed event if it wishes to display a nice error message.