Recipe 7.1 Handling Errors at the Method Level

     

7.1.1 Problem

You're uncertain how to best organize your code to handle errors at the method level. In particular, you'd like to take advantage of .NET structured exception handling for dealing with errors, but you're not sure how to best implement it.

7.1.2 Solution


If potential errors are recoverable in the routine

Use a combination of Try...Catch blocks as a retry mechanism for error handling.


If useful information can be added to the exception

Create and throw a new exception with the added information.


If cleanup is required

Perform it in the finally block.


If potential errors are not recoverable in the routine

Recovery should be handled by the calling routine and its error-handling structure.

7.1.3 Discussion

Because .NET structured exception handling is so good, we recommend that you use it, or at least consider using it, with every method that you write. There are a number of ways to implement its functionality.

7.1.3.1 Basic syntax of Try...Catch...Finally

To begin with, here is the syntax of a .NET Try...Catch...Finally block in VB and C#:

 
figs/vbicon.gif
 Private Sub anyRoutine( )  Try   'Routine code in this block   Catch err As Exception   'error handling in this block   Finally   'cleanup performed in this block   End Try  End Sub 'anyRoutine 
figs/csharpicon.gif
 private void anyRoutine( ) {  try   {   // Routine code in this block   }   catch (Exception err)   {   // error handling in this block   }   finally   {   // cleanup performed in this block   }  } // anyRoutine 

The try block includes code that implements the method.

The catch block, which is optional, includes code to handle specific errors that you have identified as likely and to recover from them when that is possible.

The finally block code, which is also optional, performs any cleanup required on leaving the method, whether due to an error or not. This typically includes the closing of any open database connections and files, disposing of objects created by the method, and so on. A finally block is guaranteed to be executed, even if an exception is thrown or the code in the routine performs a return.

As noted, the catch and finally blocks are optional. There are times when you'll want to use one or the other, and times when you'll want to use both.

A try block must also contain either a catch or a finally block.


7.1.3.2 Guidelines for implementing

Developers should make use of .NET exception handling in any method where an error is possible, but the exact technique depends on the circumstances, as summarized in Table 7-1.

Table 7-1. Guidelines for Try...Catch...Finally blocks

Can errors occur?

Recoverable?

Can useful context information be added?

Cleanup required?

Recommended combination of try , catch , and finally

No

N/A

N/A

No

None

No

N/A

N/A

Yes

try and finally only

Yes

No

No

No

None

Yes

No

No

Yes

try and finally only

Yes

No

Yes

No

try and catch only

Yes

No

Yes

Yes

try , catch , and finally

Yes

Yes

N/A

N/A

try and catch only


The .NET Framework does not close database connections, files, and so on when an error occurs. This is your responsibility as a programmer, and you should do it in the finally block. The finally block is the last opportunity to perform any cleanup before the exception-handling infrastructure takes control of the application.


7.1.3.3 Additional considerations

To help you properly implement error handling in a routine, we've provided the following leading questions. Your answers can help you determine what portions of the Try...Catch...Finally block are needed. Refer to Table 7-1 for how to structure a routine based on your answers.


Can any errors occur in this routine?

If not, no special error-handling code is required. Do not shortchange the answer to this question, however, because even x = x + 1 can result in an overflow exception.


Are the potential errors recoverable in the routine?

If an error occurs but nothing useful can be done in the routine, the exception should be allowed to propagate to the calling routine. It serves no useful purpose to catch the exception and simply rethrow it. Bear in mind that this question is different from, "Are the potential errors recoverable at the application level?" For example, if the routine attempts to write a record to a database and finds the record locked, a retry can be attempted in the routine. However, if a value is passed to the routine and the operations on the value result in an overflow or other error, recovery cannot be performed in the routine but should be handled by the calling routine and its error-handling structure.


Can any useful information be added to the exception?

Exceptions that occur in the .NET Framework contain detailed information regarding the actual error. However, the exceptions do not provide any context information about what was being attempted at the application level that may assist in troubleshooting the error or providing more useful information to the user .

A new exception can be created and thrown with the added information. The first parameter for the new exception object should contain the useful context message, while the second parameter should be the original exception. The exception-handling mechanisms in the .NET Framework create a linked list of Exception objects to create a trail from the root of the exception up to the level where the exception is actually handled. By passing the original exception as the second parameter, the linked list from the root exception is maintained . For example:

 
figs/vbicon.gif
 Catch err As Exception Throw New Exception("Useful context message", _ err) 
figs/csharpicon.gif
 catch (Exception err) { throw (new Exception("Useful context message", err)); } 


Is a combination of Try...Catch blocks warranted?

A combination of Try...Catch blocks can be useful for providing a retry mechanism for error handling, as shown in Example 7-1 (VB) and Example 7-2 (C#). These examples show the use of an internal Try...Catch block within a while loop to provide a retry mechanism and an overall Try...Finally block to ensure cleanup is performed.

The catch block should not be used for normal program flow. Normal program flow code should only be placed in the try block, with the abnormal flow being placed in the catch block. Using the catch block in normal program flow will result in significant performance degradation due to the complex operations being performed by the .NET Framework to process exceptions.


The exception-handling mechanisms in the .NET Framework are extremely powerful. Whereas this example just touches on exception handling at the method level, other specific exception types can be caught and processed differently. In addition, you can create new exception classes by inheriting from the base exception classes and adding the functionality required by your applications. An example of this technique is shown in Recipe 7.4.

7.1.4 See Also

Recipe 7.4

Example 7-1. Retrying when an exception occurs (.vb)
 Private Sub updateData(ByVal problemID As Integer, _ ByVal sectionHeading As String)  Const MAX_RETRIES As Integer = 5  Dim dbConn As OleDbConnection Dim dCmd As OleDbCommand Dim strConnection As String Dim strSQL As String Dim updateOK As Boolean Dim retryCount As Integer Try 'get the connection string from web.config and open a connection 'to the database strConnection = _ ConfigurationSettings.AppSettings("dbConnectionString") dbConn = New OleDbConnection(strConnection) dbConn.Open( ) 'build the update SQL to update the record in the database strSQL = "UPDATE EditProblem " & _ "SET SectionHeading='" & sectionHeading & "' " & _ "WHERE EditProblemID=" & problemID.ToString( ) dCmd = New OleDbCommand(strSQL, _ dbConn)  'provide a loop with a try catch block to facilitate retrying   'the database update   updateOK = False   retryCount = 0   While ((Not updateOK) And (retryCount < MAX_RETRIES))   Try   dCmd.ExecuteNonQuery( )   updateOK = True   Catch exc As Exception   retryCount += 1   If (retryCount >= MAX_RETRIES) Then   'throw a new exception with a context message stating that   'the maximum retries was exceeded   Throw New Exception("Maximum retries exceeded", _   exc)   End If   End Try   End While  Finally 'cleanup If (Not IsNothing(dbConn)) Then dbConn.Close( ) End If End Try End Sub 'updateData 

Example 7-2. Retrying when an exception occurs (.cs)
 private void updateData(int problemID, String sectionHeading) {  const int MAX_RETRIES = 5;  OleDbConnection dbConn = null; OleDbCommand dCmd = null; String strConnection = null; String strSQL = null; bool updateOK; int retryCount; try { // get the connection string from web.config and open a connection // to the database strConnection = ConfigurationSettings.AppSettings["dbConnectionString"]; dbConn = new OleDbConnection(strConnection); dbConn.Open( ); // build the update SQL to update the record in the database strSQL = "UPDATE EditProblem " + "SET SectionHeading='" + sectionHeading + "' " + "WHERE EditProblemID=" + problemID.ToString( ); dCmd = new OleDbCommand(strSQL, dbConn);  // provide a loop with a try catch block to facilitate retrying   // the database update   updateOK = false;   retryCount = 0;   while ((!updateOK) & (retryCount < MAX_RETRIES))   {   try   {   dCmd.ExecuteNonQuery( );   updateOK = true;   } // try     catch (Exception exc)   {   retryCount ++;   if (retryCount >= MAX_RETRIES)   {   // throw a new exception with a context message stating that   // the maximum retries was exceeded   throw new Exception("Maximum retries exceeded",   exc);   }   } // catch   } // While  } // try finally { // cleanup if (dbConn != null) { dbConn.Close( ); } } // finally } // updateData 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2006
Pages: 179

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net