Best practices for Error handling and Callbacks
Are there any best practices for placing error handling blocks?
This is a VB winforms scenario. Is it best to place the error handling in the client form, or middle tier / business logic side?
Thanks
Are there any best practices for placing error handling blocks?
This is a VB winforms scenario. Is it best to place the error handling in the client form, or middle tier / business logic side?
Thanks
This is a question with many answers, and whatever practice you choose to adopt will probably evolve with experience.
As a general rule of thumb I always work on the principle that I only trap exceptions if I can resolve the cause. Indeed, this is the pattern recommended by the FXCop team.
A number of languages provide a concept of checked and unchecked exceptions which may well be worth your investigation. It's a nice pattern to adopt and leads to cleaner code generation.
In short, and unchecked exception relates to a bug condition .. so you should never code to trap these. Some languages don't even give you the option of catching them.
Checked exceptions relate to invalid state (dodgy data etc) and so if you can trap them, change the data and try again, do so. If you cannot guarantee the state of your application is stable, let it go and let your application terminate before it does any irrecoverable harm. Most of the time, if I am calling some code that I know can throw a checked exception, I will test for the error condition before calling it. I find this TryDo pattern to be very easy to adopt if you don't go overboard with it.
As I said already, this is just the approach I have adopted after many years of coding in numerous languages. I hardly ever utilise Try Catch blocks (albeit I tend to live in the middle tier codebase). This approach can sometimes mean I have to think hard about resource clean up etc, but you soon learn to use more consistent patterns that do not suffer from such issues.
Sorry I can't definitively answer your question, but if you have more specific queries in this area, I'll try to help.
Richard
My typical design is to have 'silent' exception handlers in lower level components that return False or Nothing to the calling client and also have a LastException property that makes the exception available to the caller if that information is wanted. That way, the component itself doesn't have to know what the client wants to do in case of a problem and this allows the same component to be used in WinForms, ASP.NET, or Service applications with no modification.
For the UI, if the error is unrecoverable, I'll send the user to a "Bug Report" form where details on the problem can be gathered and mailed and/or stored. Otherwise, I'll retry or continue, usually without alerting the user, depending on the situation. The idea here is to keep it as friendly as possible to the user. For service applications, I'll usually set them up to auto-email and/or write to the system log.
Frank Carr wrote:
My typical design is to have 'silent' exception handlers in lower level components that return False or Nothing to the calling client and also have a LastException property that makes the exception available to the caller if that information is wanted. That way, the component itself doesn't have to know what the client wants to do in case of a problem and this allows the same component to be used in WinForms, ASP.NET, or Service applications with no modification.
Frank, could you possibly post sample code / or examples? I like your idea, but wanted to get a clearer understanding of what you are doing.
Thanks
MSNetDeveloper wrote:
Frank, could you possibly post sample code / or examples? I like your idea, but wanted to get a clearer understanding of what you are doing.
Here's a little code that shows this that I snipped from my database layer
Protected LastSqlCommandProperty As SqlCommand
Protected LastExceptionProperty As Exception
Public Function ExecuteNonQuery(ByVal nonQueryCommand As SqlCommand) As Integer
Dim ReturnValue As Integer
Try
OpenConnection()
LastSqlCommandProperty = nonQueryCommand
ReturnValue = nonQueryCommand.ExecuteNonQuery
Catch ex As Exception
LastExceptionProperty = ex
ReturnValue = -1
Finally
CloseConnection()
End Try
Return ReturnValue
End Function
Public ReadOnly Property LastException() As Exception
Get
LastException = LastExceptionProperty
End Get
End Property
Public ReadOnly Property LastSqlCommand() As SqlCommand
Get
LastSqlCommand = LastSqlCommandProperty
End Get
End Property
In the example, ExecuteNonQuery is used to call stored procs that don't return any data, only a records affected count. The routine returns a -1 on error, 0 if no records are processed, and the count of affected records if there are any. If a -1 is returned, the caller can then check the LastException property, which gets set internally in the Catch code, to determine the cause of the error and what to do from there. I also save the last command as well since I've found this handy for both error checking and repeating similar calls. The properties are ReadOnly but the underlying variable is Protected in order to allow classes that inherit access to the variables in their own routines.
The caller's code might look something like this:
Select Case myDB.ExecuteNonQuery(mySqlCommand)
Case -1
'something went wrong so deal with the exception
DealWithComponentError(myDB.LastException)
Case 0
'no records affected, nothing else to do
Case Else
'everything's OK do some more processing here
End Select