DEV363
Exception Management Best Practices in .NET Applications
Ron JacobsProduct ManagerMicrosoft Patterns & Practices
Agenda
Exceptions and Exception Management Overview
Exception Detection and Propagation
Exceptions and Threads
Custom Exceptions
Managing Unhandled Exceptions
Logging Exceptions
Exception Notification
What is an Exception?
Any error condition or unexpected behavior
Exceptions can be raised because of a fault in your code or in code you call
Unavailable operating system resources
Unexpected conditions the CLR encounters (such as code that cannot be verified)
Applications can recover from some of these conditions, but not others.
While you can recover from most application exceptions, you cannot recover from most runtime exceptions.
Why Exceptions?
Exceptions offer several advantagesReturn values may be ignored but exceptions do not go unnoticed
Invalid values do not continue to propagate through the system
You do not have to check return codes
Exception-handling code can be easily added to increase program reliability
What is Exception Management
A strategy for Detecting exceptions
Logging and reporting information
Generating events that can be monitored externally to assist system operation
Exceptions HappenWhen you least expect them
Plan for them
Test your strategy
Exception Handling
Yes
Exceptiondetected?
Can recover?
Yes
Propagateexception
Performcleanup
Add any contextualinformation
No
No
Exceptionpropagated to your
method Exceptionpropagates to
caller
Recover fromexception
Unhandled Exceptions
Gatherinformation
Log errorInformation
Send anynotification
Sync or Async
Perform anycleanup
Unhandledexception
Displayinformation
to user
Sync or Async
Notification
Data store
Structured Exception Handling
All .NET languages use structured exception handling
Structured exception handling provides a control structure that includes exceptions, protected blocks of code, and filters
// C# or J#try{ // Some code that could throw an exception.}catch(SomeException exc){ // Code to react to the occurrence of the exception}finally{ // Code that gets run always, whether or not an // exception was thrown. This is usually clean up // code that should be executed regardless of // whether an exception has been thrown.}
// C# or J#try{ // Some code that could throw an exception.}catch(SomeException exc){ // Code to react to the occurrence of the exception}finally{ // Code that gets run always, whether or not an // exception was thrown. This is usually clean up // code that should be executed regardless of // whether an exception has been thrown.}
' VB.NETTry ' Some code that could throw an exception.Catch exc As SomeException ' Code to react to the occurrence of the exceptionFinally ' Code that gets run always, whether or not an ' exception was thrown. This is usually clean up ' code that should be executed regardless of ' whether an exception has been thrown.End Try
' VB.NETTry ' Some code that could throw an exception.Catch exc As SomeException ' Code to react to the occurrence of the exceptionFinally ' Code that gets run always, whether or not an ' exception was thrown. This is usually clean up ' code that should be executed regardless of ' whether an exception has been thrown.End Try
When to Catch Exceptions
Catch exceptions when you require:Information for logging
Extra information relevant to the exception
Cleanup code execution
Exception recovery
Other code should allow exceptions to propagate up the call stack
When to Throw Exceptions
Throw exceptions when conditions outside your code's assumptions occur
Do not use exceptions to provide intended functionality
Entering an invalid user password for a logon is not unexpected; do not throw an exception
If the database is unavailable, throw an exception
Exception Interception
Exceptions thrown may be caught by the CLR and another exception thrown up the call stack in place of the original
ArrayList.Sort calls IComparable.CompareTo
Exceptions in this method are intercepted and the CLR throws a System.InvalidOperationException
The InnerException property of the newer exception contains the original exception
Propagating Exceptions
Propagation options:
Catch, recover, continue – exception does not propagate
Let the exception propagate automatically
Try / Finally without catch
Catch and re-throw the exception
Catch, wrap, and throw the wrapped exception
Wrapping Exceptions
The original exception is wrapped in a new exception
The new exception is thrown up the call stack
A() B() C()
Originalexception
Originalexception
Wrapper exception
Call Call
ThrowThrow
// C#try{ // Some code that could throw an exception.}catch(TypeAException e){ // Code to do any processing needed. // Rethrow the exception throw;}catch(TypeBException e){ // Code to do any processing needed. // Wrap the current exception in a more relevant // outer exception and rethrow the new exception. throw(new TypeCException(strMessage, e));}finally{ // Code that gets executed regardless of whether // an exception was thrown.}
// C#try{ // Some code that could throw an exception.}catch(TypeAException e){ // Code to do any processing needed. // Rethrow the exception throw;}catch(TypeBException e){ // Code to do any processing needed. // Wrap the current exception in a more relevant // outer exception and rethrow the new exception. throw(new TypeCException(strMessage, e));}finally{ // Code that gets executed regardless of whether // an exception was thrown.}
Propagation Example
// J#try{ // Some code that could throw an exception.}catch(TypeAException e){ // Code to do any processing needed. // Rethrow the exception throw e;}catch(TypeBException e){ // Code to do any processing needed. // Wrap the current exception in a more relevant // outer exception and rethrow the new exception. throw(new TypeCException(strMessage, e));}finally{ // Code that gets executed regardless of whether // an exception was thrown.}
// J#try{ // Some code that could throw an exception.}catch(TypeAException e){ // Code to do any processing needed. // Rethrow the exception throw e;}catch(TypeBException e){ // Code to do any processing needed. // Wrap the current exception in a more relevant // outer exception and rethrow the new exception. throw(new TypeCException(strMessage, e));}finally{ // Code that gets executed regardless of whether // an exception was thrown.}
' VB.NETTry ' Some code that could throw an exception.Catch ex As TypeAException ' Code to do any processing needed. ' Rethrow the exception ThrowCatch ex As TypeBException ' Code to do any processing needed.
' Wrap the current exception in a more relevant ' outer exception and rethrow the new exception. Throw New TypeCException(strMessage, ex)Finally ' Code that gets executed regardless of whether ' an exception was thrown.End Try
' VB.NETTry ' Some code that could throw an exception.Catch ex As TypeAException ' Code to do any processing needed. ' Rethrow the exception ThrowCatch ex As TypeBException ' Code to do any processing needed.
' Wrap the current exception in a more relevant ' outer exception and rethrow the new exception. Throw New TypeCException(strMessage, ex)Finally ' Code that gets executed regardless of whether ' an exception was thrown.End Try
' VB.NET' Declare outside of the try blockDim myConnection As SqlConnectionTry myConnection = New SqlConnection(connStr) ' Use the connection ' ...Finally ' We must always close the connection if it was opened If Not myConnection Is Nothing Then myConnection.Close() End IfEnd Try
' VB.NET' Declare outside of the try blockDim myConnection As SqlConnectionTry myConnection = New SqlConnection(connStr) ' Use the connection ' ...Finally ' We must always close the connection if it was opened If Not myConnection Is Nothing Then myConnection.Close() End IfEnd Try
// C# / J#// Declare outside of the try blockSqlConnection myConnection = null;try{ myConnection = new SqlConnection(connStr); // Use the connection // ...}finally{ // We must always close the connection if it was opened if (myConnection != null) myConnection.Close();}
// C# / J#// Declare outside of the try blockSqlConnection myConnection = null;try{ myConnection = new SqlConnection(connStr); // Use the connection // ...}finally{ // We must always close the connection if it was opened if (myConnection != null) myConnection.Close();}
Try / Finally Example
// C#using(SqlConnection myConnection =
new SqlConnection(connStr)){ // Use the connection // When the code exits this block, SqlConnection.Dispose // will be called and will close the connection}
// C#using(SqlConnection myConnection =
new SqlConnection(connStr)){ // Use the connection // When the code exits this block, SqlConnection.Dispose // will be called and will close the connection}
Propagation Options
Adding relevancy is key for troubleshooting
Reacting is important for cleanupReact to the exception
Add relevancy
Propagate automatically
No No
Try / Finally Yes NoCatch and rethrow Yes NoCatch, wrap, and throw
Yes Yes
When to use InnerException
Wrap exceptions only when there is a compelling reason to do so
Wrapping exceptions can provide a more relevant exception to the caller
FileNotFoundException is not relevant to callers of a LoadUserInfo method, even if the exception occurs internally
A FailedToLoadUserInfoException custom exception provides more relevant information to LoadUserInfo's caller
Exceptions & Threads
Exceptions do not propagate to calling thread
Exceptions can break the “protocol” of intra-thread communication
Catch exceptions in your thread procedure
Catch ThreadInterruptedException to cleanup resources
Exception Events
Application.ThreadExceptionHandles exceptions thrown from the main thread running the window procedure
AppDomain.UnhandledExceptionProvides default handling for uncaught exceptions from any thread
Last chance to clean up before CLR terminates
Fun With Exceptions
demodemo
Exception Hierarchies
Exception classes derive from System.Exception
Your application exceptions should derive from ApplicationException
Object
Exception
SystemException
SystemException Classes
ApplicationException
Your Exception Classes
Custom Exception Hierarchy
Create custom exceptions based on ApplicationException
Object
Exception
ApplicationException
Your Exception Classes
Your Application's BaseException Class
Your Exception Classes
Your Exception Classes
Your application'sexception hierarchy
Custom Exception Benefits
Properties and methods defined on the base application exception class are inherited by custom exception classes
New exception classes created after deployment can derive from existing exception types
Custom Exception Design
Does an exception exist for this condition?If so, use that exception class
Does a particular exception need discrete handling?
If so, create an exception class to allow catching of the specific exception
Do you need specific behavior or additional information for a particular exception?
If so, create a new exception class include additional information or functionality
Creating a Custom Exception Class
Always end custom exception classes with "Exception“
Implement the three standard exception constructors
// C#using System;public class YourBaseApplicationException : ApplicationException{ // Default constructor public YourBaseApplicationException () { } // Constructor accepting a single string message public YourBaseApplicationException (string message) : base(message) { } // Constructor accepting a string message and an inner // exception to be wrapped by this custom exception class public YourBaseApplicationException(string message, Exception inner) : base(message, inner) { }}
// C#using System;public class YourBaseApplicationException : ApplicationException{ // Default constructor public YourBaseApplicationException () { } // Constructor accepting a single string message public YourBaseApplicationException (string message) : base(message) { } // Constructor accepting a string message and an inner // exception to be wrapped by this custom exception class public YourBaseApplicationException(string message, Exception inner) : base(message, inner) { }}
' VB.NETPublic Class YourBaseApplicationException Inherits ApplicationException
Public Sub New() MyBase.New() End Sub
Public Sub New(ByVal message As String) MyBase.New(message) End Sub
Public Sub New(ByVal message As String, ByVal innerException As Exception) MyBase.New(message, innerException) End SubEnd Class
' VB.NETPublic Class YourBaseApplicationException Inherits ApplicationException
Public Sub New() MyBase.New() End Sub
Public Sub New(ByVal message As String) MyBase.New(message) End Sub
Public Sub New(ByVal message As String, ByVal innerException As Exception) MyBase.New(message, innerException) End SubEnd Class
Base Application Exception
Create a single base exception class that derives from ApplicationException
Add fields to this base class to capture common information such as:
Date and time the exception occurred
Machine name
User name, etc.
Remoting Exceptions
The Exception class implements ISerializable
Use the Serializable attribute to provide serialization
[Serializable] public class ExampleException : ApplicationException{ // standard constructors as before...
protected ExampleException(SerializationInfo info, StreamingContext context) : base(info,context) { m_strName = info.GetString("m_strName"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("m_strName", m_strName, typeof(String)); base.GetObjectData(info,context); } // remainder of class with fields etc.}
[Serializable] public class ExampleException : ApplicationException{ // standard constructors as before...
protected ExampleException(SerializationInfo info, StreamingContext context) : base(info,context) { m_strName = info.GetString("m_strName"); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("m_strName", m_strName, typeof(String)); base.GetObjectData(info,context); } // remainder of class with fields etc.}
Unhandled Exceptions
Before returning to the user, unhandled exceptions must be logged and notifications sent
In Web-based applications, this final boundary is controlled by ASP.NET code
AppDomain.UnhandledException
Application.ThreadException for UI threads in WinForms
Configuring Web.Config Settings
Use <customErrors> to control exception redirection
Allows redirection for HTTP error codes, but only for ASP.NET related files
Three modes available for the default redirection:on – Unhandled exceptions redirect the user to the specified page
off – Users see exception information and are not redirected
remoteonly – Users accessing the site on the local machine see exception information, all others are redirected
<customErrors defaultredirect="http://hostname/error.aspx" mode="on"> <error statuscode="500" redirect="/errorpages/servererror.aspx" /> <error statuscode="404" redirect="/errorpages/filenotfound.htm" /></customErrors>
<customErrors defaultredirect="http://hostname/error.aspx" mode="on"> <error statuscode="500" redirect="/errorpages/servererror.aspx" /> <error statuscode="404" redirect="/errorpages/filenotfound.htm" /></customErrors>
Using the @Page Directive
Web.config settings apply to its directory and any child directories
The ErrorPage attribute of the @Page directive overrides Web.config for specific pages
<%@ Page ErrorPage="customerror.aspx" %><%@ Page ErrorPage="customerror.aspx" %>
Managing Exceptions in ASP.NET CodeTwo events for reacting to unhandled exceptions:
Page_Error – Fired when an exception is not handled within the page
Application_Error – Located in the global.asax file; occurs if exception has not been handled by any application code
Page.Error += new System.EventHandler(Page_Error);Page.Error += new System.EventHandler(Page_Error);
// In global.asax fileprotected void Application_Error(Object sender, EventArgs e){ Exception exc = Server.GetLastError(); // Perform logging, send any notifications, etc.}
// In global.asax fileprotected void Application_Error(Object sender, EventArgs e){ Exception exc = Server.GetLastError(); // Perform logging, send any notifications, etc.}
Managing Exceptions in Web Services
The SoapException class provides exception information to Web service clients using SOAP
Unhandled exceptions from a Web service result in a SoapException
.NET-based clients can capture exception information using standard exception handling techniques
The Message property includes the original exception information
Hiding Exception Information in Web Services
Returning full exception information to external Web service clients may not be desirable
Create a generic application exception containing only the appropriate information
Catch unhandled exceptions and throw the generic application exception
Use the UnhandledExceptionEventHandler delegate to associate unhandled exceptions to response code
The Application_Error event provides the same ability in ASP.NET applications
Gathering Information
Ensure all the appropriate information that accurately represents the exception condition is capturedRecipients of this information could include:
End users – Require a meaningful and well presented descriptionApplication developers – Require more detailed information to assist with problem diagnosisOperators – Require relevant information allowing them to react appropriately and take necessary recovery steps
Accessing All Exception Data
Exceptions can have various structures and expose different public properties
Use reflection (System.Reflection namespace) to interrogate all properties of any exception object
Useful for generic logging routines that accept Exception objects
Options for Logging Exceptions
Log the information in a reliable way
Options include: The Windows event log
A central relational database
A custom log file
Using the Windows Event Log
Windows provide various event logs:The operating system places error details in the System event log
Applications use the Application event log
Security information is logged in the Security event log
Custom logs can be created rather than using the shared application event log
The .NET Framework provides event log related classes
Windows Event Log
Highly reliableConfigurable log file size managementTools allow viewing and manipulation of log entries.NET Framework classes make the event log easy to write to and maintain programmatically – e.g. System.Diagnostics.EventLog classMost monitoring tools support the event log as an event sourceTight integration with Windows Management Instrumentation (WMI)Systems administrators and operators are familiar with the event logThe operating system and many applications use the event log, making it easy to compare event sequences
Advantages
Windows Event Log
Each machine logs to a local event logCauses monitoring and maintenance problems for large server farms
You can write entries to a log on another machine; increases the risk of failure
Disadvantages
Using a Central Database
Information is stored in a centralized location and is accessible from remote machines
Use database tools for querying and reporting the error data
Structure storage of the information in application-specific tables
Advantages
Using a Central Database
If the database is unavailable (for example, network failure), you risk losing information
Write information to the local event log if the database update fails to guarantee information is logged
Storing application entries remotely from operating system entries make comparison of event sequences difficult
You must develop tools to view and administer the data
You must customize monitoring systems to interact with the database
Disadvantages
Using a Custom Log File
Complete flexibility when choosing the log format
Advantages
Using a Custom Log File
Building custom logging mechanisms that handle concurrent access is not simple
You must create and implement processes to manage the log size
You must develop tools to view and administer the log files
Causes monitoring problems for server farms because many monitoring tools cannot aggregate custom log files
Monitoring tools must be configured to monitor the log files
Disadvantages
Asynchronous Delivery with Message Queuing
Microsoft Message Queuing (MSMQ) provides a reliable transport mechanism to deliver data asynchronously
Message Queuing ensures data is not lost or duplicated
Message Queuing requires a larger coding effort
Messages may not arrive to the final store in the order in which they occurred
Data may not be stored immediately
Handling Notification of Exceptions
Without proper notification, exceptions can go undetected
You should not have to change code every time you need to modify your notification mechanism
For example, altering the list of recipients
Applications should communicate error conditions and rely on a decoupled monitoring system to identify errors and take appropriate action
Working with a Monitoring ApplicationWhen working with a monitoring system:
Your application can rely on the monitoring system to watch your log and deal with the detected error conditions
You can implement code to raise events to notify the monitoring system
Windows Management Instrumentation (WMI) is the preferred method for notifying a monitoring system
Monitoring the Log Store You can configure monitoring systems to watch your log and monitor the arrival of messagesAllows watching for particular messages or those that match a criterionAdvantages:
The monitoring system is decoupled from your application codeNo additional code beyond your logging implementation is required
Disadvantages:The monitoring system must be configured to poll the log and filter records to find those it needs to handleOther systems that want to handle these events must be configured to watch your chosen log
Creating WMI Events
.NET provides WMI-related classes within the System.Management and System.Management.Instrumentation namespacesAdvantages:
Decouples the operational procedures carried out as a result of an unhandled exception from your application code Integrates well with monitoring applicationsAllows monitoring applications to group your application events with other application events to provide an enterprise wide notification system
Disadvantages:Applications can raise WMI events from multiple machines, requiring the monitoring tool to be installed on each machine
Creating Notifications Manually If monitoring is not available, you can create
notifications by:
Sending mail using SMTP
Developing a custom notification system
Sending Mail Using SMTP
Send notifications using SMTP to a distribution list that includes the appropriate support personnel
Advantages of using SMTP for notification:Quick and easy to implement using SMTP
.NET provides the System.Web.Mail namespace
Disadvantages of using SMTP for notification:If the e-mail fails, the notification is lost
When the number of errors is high, many e-mails can be sent, making it difficult to solve the problem
Custom Notification System
Custom notification programs can receive messages from your application and take some action based on the exception
Message Queuing is ideal for passing data to the notification system
Advantages of using custom notification:Decouples the notification procedures from your application code
Supports multiple applications
Overcomes licensing issues with monitoring systems
Provides asynchronous, reliable delivery of notifications
Custom Notification System
Disadvantages:You must develop and maintain the notification system
Similar functionality is already provided in production monitoring systems such as Application Center Server
Interoperability
The CLR provides interoperability features that allow managed and unmanaged code to work together
When a COM client calls managed code that throws an exception, the CLR translates it into an HRESULT
You may need to change the HResult property of the exception object before propagating it to the COM client
Interoperability
If a COM component returns a failed HRESULT to managed code, the CLR creates an exception from the HRESULT
Additional information provided by the COM IErrorInfo interface is transferred into the exception object
Localization
Provide localization to ensure information is presented in a format your audiences can understand
Can use .NET localization mechanisms:Culture – part of the assembly identity that is involved in the binding process
Satellite Assemblies – resource only assemblies that do not contain Common Intermediate Language
Sharing Code through CommunityGotDotNet Workspaces
Community Blocks
Workspaces for blocks Community Blocks Share ideas / extensions File bugs, fix them too!
GotDotNetMicrosoft
Blocks
Internet
Next Steps
Use patterns & practicesDownload & Use Application Blocks
Participate in Workspaces
Provide us with your feedback:Are we focused on the topics you need?
What other suggestions or requests do you have? [email protected]
http://msdn.microsoft.com/practices
Recommended Session
DEV333 Instrumenting Applications for Manageability with the Enterprise Instrumentation Framework
Thursday 3 July – 18:15 – Auditorium
Community Resources
Community Resourceshttp://www.microsoft.com/communities/default.mspx
Most Valuable Professional (MVP)http://www.mvp.support.microsoft.com/
NewsgroupsConverse online with Microsoft Newsgroups, including Worldwidehttp://www.microsoft.com/communities/newsgroups/default.mspx
User GroupsMeet and learn with your peershttp://www.microsoft.com/communities/usergroups/default.mspx
Suggested Reading & Resources
Exception Management Architecture Guide
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/exceptdotnet.asp
evaluationsevaluations
© 2003 Microsoft Corporation. All rights reserved.© 2003 Microsoft Corporation. All rights reserved.This presentation is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.This presentation is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.
Top Related