The ASP.NET 2.0 Anthology: 101 Essential Tips, Tricks & Hacks
How can I handle exceptions in my code?
Very few developers get exception handling right the first time. It’s all too common to find code like this:
try
{
// do something that may cause an error
}
catch (Exception ex)
{
// handle any exception here
}
This is rarely the correct strategy. There are some circumstances in which you may need to catch every exception, but you should avoid doing so whenever possible.
Solution
Here are some guidelines that I’ve found useful when dealing with exceptions in my own code:
The golden rule of exception handling: unless you have a good reason to catch an exception, don’t!
It’s okay to catch exceptions in exceptional conditions. Exceptions are supposed to be exceptional, as the dictionary definition for the word (uncommon, unusual) indicates. When in doubt, let the calling routine—or the global exception handler—deal with the problem. The exceptions that are the most difficult to troubleshoot are those that don’t exist, because a developer upstream from you decided it was a good idea to consume the exception, leaving you with nothing but a broken application and no visible symptoms. Once you’ve had to troubleshoot one of these monsters, you’ll vow to never catch exceptions blindly again. So, always remember: when in doubt, do not catch any exceptions.
Catch the exception if you can correct the problem that it implies.
For example, if you try to write to a file that’s marked as read-only, try removing the read-only flag from the file. In this case, you’d handle the exception and fix the problem, so you should eat the exception. It doesn’t exist, because you fixed it.
Catch the exception if you can provide additional information to the user.
For example, if your application fails to connect via HTTP to a remote web site, you could provide details about why the connection failed. Was the DNS invalid? Did it time out? Was the connection closed? Did the site return 401 Unauthorized, which implies that credentials are needed? In these cases you want to catch the exception, and re-throw it as an inner exception with more information. This is a very good reason to catch an exception, but note that we are still re-throwing it.
Catch specific exceptions, but let the rest pass through.
Avoid catching System.Exceptionwhenever possible; try to catch only the errors that are specific to a given block of code, and let the truly unusual rest become unhandled exceptions.
Of course, there will be times when you’ll want to bend these rules for completely legitimate reasons—but at least consider the rules before you break them!
Also, if you need to re-throw an exception, be careful how you do it. Always re-throw the exception using the throw keyword with no parameters, like so:
try
{
command.Execute();
TransactionManager.Commit();
}
catch(Exception exception)
{
TransactionManager.Rollback();
throw;
}
Remember that the stack trace is created at the time you throw the exception, so using the throw keyword on its own like this (rather than using throw exception) preserves the stack trace of the original exception.
How can I handle errors in my web site?
This is one of the first questions you should think about when you set out to develop a web application. ASP.NET doesn’t provide you with a global exception handling strategy out of the box. Fortunately, it does provide several different approaches for handling global exceptions in code. Each approach comes with its own advantages and disadvantages.
Solutions
The three options you can use to handle exceptions are:
? using the built-in health monitoring support
? setting exception handling settings in a global.asax and in the Web.config file
? using the HttpModule class
Let’s look at each in turn.
Handling Exceptions Via Health Monitoring
One of the easiest ways to implement a global exception handler is to use the built-in ASP.NET health monitoring support.
Check the Event Log
In the absence of any other unhandled exception strategy, ASP.NET 2.0 does in fact log unhandled exceptions to the event log. It’s not my favorite place to dig around for errors, but it’s better than nothing.
To enable automatic email notifications of unhandled exceptions via health monitoring, simply add this section to your Web.config file
Web.config
<healthMonitoring enabled="true"> <providers> <add name="MailProvider" type="System.Web.Management.SimpleMailWebEventProvider" from="webserver@example.com" to="you@example.com" subjectPrefix="Unhandled Exception: " bufferMode="Critical Notification" /> </providers> <rules> <remove name="All Errors Default"/> <remove name="Failure Audits Default"/> <add name="All Errors Default" eventName="All Errors" provider="MailProvider" /> </rules> </healthMonitoring>
For this code to work, you’ll also need to set up System.Net.Mail SMTP email support in your Web.config. Here’s a representative section that enables the most basic SMTP settings for a server named localhost:
Web.config
<system.net> <mailSettings> <smtp> <network host="localhost" /> </smtp> </mailSettings> </system.net>
And that’s it! Now every unhandled exception will automatically generate an email that’s sent to you. The email, shown in Figure 13.1, is not a paragon of great formatting, but it gets the job done.
And that’s it! Now every unhandled exception will automatically generate an email that’s sent to you. The email, shown in Figure 13.1, is not a paragon of great formatting, but it gets the job done.
Of course, ASP.NET doesn’t limit you to using email notifications—you can use any of the built-in health monitoring event providers to direct your exception information to one or more of the following providers:
?
EventLogWebEventProvider: This class writes events to the window’s event log for posterity. Writing exceptions to this class is enabled by default.
?
SqlWebEventProvider: This class writes events to your application’s database.
?
WmiWebEventProvider: This class publishes events to the Windows Management Instrumentation. Other applications can consume these WMI events in order to alert system administrators that an exception has occurred.
?
SimpleMailWebEventProvider: This class sends an email (for example, to a system administrator) in response to application health events.
? TraceWebEventProvider: This class publishes events to the System.Diagnostics.Trace object. This data can then be collected by a TraceListener for debugging purposes.
Conspicuously absent from that list is any sort of disk or file destination. You could write your own provider, but that defeats the no-code advantage of the health monitoring solution. Luckily, armed with the providers in the above list, we have several good alternatives to writing to a file. The official MSDN documentation for contains more information about these and other related classes.
Specifying Exception Handling in the global.asax and Web.config Files
The conventional way that you should implement a global exception handler is through global.asax and Web.config.
If your project doesn’t contain a global.asax file, add one by accessing the Add New Item menu and selecting Global Application Class. Then locate the Application_Error method in this file and modify it to capture the error:
void Application_Error(object sender, EventArgs e)
{
Exception ex =
HttpContext.Current.Server.GetLastError();
if (ex != null)
{
ErrorHandler.HandleException(ex);
}
}
Next, create a new static class called ErrorHandler and add to it a new method called HandleException that deals with the exception:
public static class ErrorHandler
{
public static void HandleException(Exception ex)
{
if (ex == null)
return;
Exception exceptionLayer = null;
if (ex is HttpUnhandledException)
{
if (ex.InnerException != null)
exceptionLayer = ex.InnerException;
}
else
{
exceptionLayer = ex;
}
StringBuilder sb = new StringBuilder();
while (exceptionLayer != null)
{
sb.AppendLine(ex.ToString());sb.AppendLine("------------------------");
exceptionLayer = exceptionLayer.InnerException;
}
Log(sb.ToString());
}
}
The code is relatively straightforward. We make a point of discarding the outer HttpUnhandledException—it’s a standard wrapper that comes with every ASP.NET exception. We have to peel that layer away to get to the InnerException (and its InnerException, and so on) that contains the real error.
Notice that, at this point in the code, I haven’t populated the generic Log method, so the exception isn’t being passed anywhere just yet. You could easily write your own Log method to send the exception via email, log it to a file, and so forth. But rather than write a whole lot of extra code, you might want to save some time by handing the exception off to the open source Log4net logging framework, which we’ll cover in the section called “What’s the best way to write a log file?” later in this chapter.
So we’ve satisfied our professional obligations as programmers, but what about those poor old users? It’s bad form to let them see the Yellow Screen of Death shown in Figure 13.2.
If we’re going to the trouble of implementing a global exception handling strategy behind the scenes, we might as well go the final mile and implement a friendlier user interface for users who are unlucky enough to encounter an exception.
Implementing a custom error page is as simple as editing Web.config and adding a customErrors element within the system.web section that points to your custom file:
Web.config
<system.web> <customErrors mode="On" defaultRedirect="~/Error.aspx"> </customErrors> ? </system.web>
As you can see, in this case, doing the right thing by users is very easy. They’re already disappointed because your web site has crashed—don’t make matters worse by scaring them with the awful default ASP.NET error page.
Handling Errors Via HttpModule
There’s nothing wrong with the methods we’ve discussed so far, but by far the most flexible way to implement global exception handling is to use the HttpModuleclass.
First, let’s create a class library project containing a new HttpModule:
public class ExceptionHandlingModule : IHttpModule
{
public void Init(System.Web.HttpApplication app)
{
app.Error += new EventHandler(OnError);
}
private void OnError(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
ErrorHandler.HandleException(app.Server.GetLastError());
}
public void Dispose()
{
// Nothing to Dispose() }
}
Clean Up After Yourself!
It’ s not relevant in this example, but it’s worth mentioning: if you’re planning to present a custom interface to the user via code that utilizes the Response.Write method or something similar, you must clear the existing error before continuing. The following line of code will take care of that:
app.Server.ClearError();
Compile that class library, and add a reference to it from your web project or web site. Then edit Web.config as follows to reference our brand new HttpModule:
<httpModules> <add type="ExceptionHandlingModule, ExceptionLibrary" name="ErrorHandler" /> </httpModules>
The big advantage of this approach is that it doesn’t require you to make any code changes in the web site that uses it.
The compiled HttpModule, along with the error handling logic, can be delivered in a single DLL file. Simply add that file to the web site path, then modify Web.config, and you have a perfectly portable and reusable global exception handling strategy to use across all of your web sites.
How can I use a pre-built exception handling strategy?
The exception handling strategies we explored earlier all require some coding on our part to deliver a complete solution. Luckily, there’s a pre-built global exception handler that we can employ to avoid doing all that extra work—it even uses the flexible HttpModule model.
For a robust, global exception handling strategy that you can easily plug into a web site, look no further than the ELMAH (Error Logging Modules And Handlers) solution.2
Add a reference to the ELMAH binary in your web site or web application project. To enable ELMAH, edit your Web.config file and add a custom configuration section that instructs ELMAH on how we want it to handle errors
ELMAH supports a number of logging configurations, including the use of a SQL Server database, a MySQL database, and an XML file, but for this example we’re going to use the in-memory database. We’ll also want an email to be sent asynchronously when an error occurs.
Since we’re sending mail, we’ll need to add the SMTP configuration block for System.Net.Mail to our Web.config file
Once this code is in place, fire up your web site and trigger an exception. I often find it useful to include a hidden method for generating an exception on any deployed web site, so I can periodically check and make sure the global exception handling is working properly.
Although ELMAH doesn’t provide an interface for the unfortunate user who’s experiencing the exception, it does provide an excellent web interface for developers to view exceptions. Simply browse to http://localhost:nnnnn/elmah/default.aspx (where localhost and nnnnn are replaced by your server and port number, respectively). From here you can browse through all the exceptions that have occurred in your web application
The interface lets you view details for each individual error. You can also subscribe to the RSS feed for this page, so that you can monitor your site’s errors in the RSS feed reader of your choice.
Since we opted to use the in-memory logger, rather than a more permanent one, we can only view exceptions that have occurred since our application was last started. But this is usually enough for troubleshooting purposes, as we always have the exception email to fall back on.
What’s the best way to write a log file?
One of the most popular solutions for logging debug information and exceptions is the log4net framework.3 This open source logging framework is part of the popular log4j Java logging framework, yet, despite its popularity, many developers still struggle to get log4net to work properly within the context of an ASP.NET web site.
In this solution, I’ll step you through the task of setting up the log4net framework. We’ll use the best-practice technique of creating a separate configuration file, rather than dumping the log4net settings into Web.config.
Solution
First, add the log4net.dll reference to your web application or web site project.
With this file in place, we need to set up a log4net configuration file. While it would be possible to specify the log4net settings in Web.config, this is not the most desirable place to locate these settings. Any update to Web.config causes the AppDomain to recycle, which causes a performance hit as the sessions are dropped, cache is cleared, and pages are recompiled. So even a small change to a log4net setting would effectively restart the entire web site.
However, if we put our settings in a separate log4net configuration file, log4net will attach a FileSystemWatcher to that file and reload the logging settings any time that file changes, without reloading the AppDomain.
While this is a matter of preference, I like to put my log4net settings in a file named log4net.config
This sets up a RollingLogFileAppenderto receive logging messages. As you might have guessed by the name, the RollingLogFileAppender logs messages to a file, rolling over to a new file every day, week or month, depending on your settings. For a more in-depth understanding of these settings, refer to the configuration section of the log4net manual.4
Now we need to tell log4net where its configuration file is located. We can do this in two ways. For Web Application projects, you can use an XmlConfiguratorattribute within AssemblyInfo.cs
Filed under: .NET, Architecture, ASP.NET, Design, Reference, Web Tagged: .NET, .NET4, ASP, ASP.NET, Auditing, Errors, Exceptions, Handlers, Log4Net, Logging, Practices, Tips, Tracing, Tricks, Web
Written by Visitor Blogs on June 8th, 2010 with no comments.
Read more articles on .NET4 and ASP and Logging and Architecture and Practices and Auditing and Log4Net and Handlers and Tracing and reference and tricks and .Net and Tips and Web and ASP.NET and Design and otherSoftware and errors and exceptions and Web.



