Sanity Check: Custom Exception Approach

J

Jeff S

Before I go off and paint myself into a corner by writing a bunch of code
that I later regret, I'd appreciate your feedback on my approach to
exception handling: My objective is this: in the DAL, if a runtime exception
occurs (e.g., can't connect to SQL Server), I want to log the details to an
application log, and then throw a custom exception up to the presentation
layer (Web UI in this case) that has a user-friendly message.

I defined the custom exception (which inherits from System.Exception of
course) as a public member of the DAL. The intent is to set the
user-friendly message in the DAL because that's where I know exactly what
went wrong and can translate that into what the user should do about it, if
anything.

Separately, throwing a custom exception from the DAL (as opposed to simply
rethrowing the original exception) has the benefit that the presentation
layer (PL) can easily differentiate between different types of errors (those
originating in the DAL and those originating in the PL itself) without
having multipile try... catch blocks (would instead have multipile catch
blocks). Another reason to differentiate in the PL is that the PL in my case
would assume that any DAL exceptions have already been logged and would only
log non DAL exceptions. This would get me to another objective of logging
every exception and logging each only once (not once in the DAL and then
again after rethrown to the PL).

So, what do you think? Am I totally nuts? Is this allright, or would my
approach amount to being a Rube Goldberg device?

Thanks!
 
S

Simon Johnson

Before I go off and paint myself into a corner by writing a bunch of code
that I later regret, I'd appreciate your feedback on my approach to
exception handling: My objective is this: in the DAL, if a runtime
exception
occurs (e.g., can't connect to SQL Server), I want to log the details to
an
application log, and then throw a custom exception up to the presentation
layer (Web UI in this case) that has a user-friendly message.

I defined the custom exception (which inherits from System.Exception of
course) as a public member of the DAL. The intent is to set the
user-friendly message in the DAL because that's where I know exactly what
went wrong and can translate that into what the user should do about it,
if
anything.

Separately, throwing a custom exception from the DAL (as opposed to simply
rethrowing the original exception) has the benefit that the presentation
layer (PL) can easily differentiate between different types of errors
(those
originating in the DAL and those originating in the PL itself) without
having multipile try... catch blocks (would instead have multipile catch
blocks).

This confuses me a little. The PL will have a different class of exceptions
to the DAL. You wouldn't get an SqlException
coming from the DAL. Unless you're talking about the fact that similar
exceptions could come from both the DAL and the PL.
Another reason to differentiate in the PL is that the PL in my case
would assume that any DAL exceptions have already been logged and would
only
log non DAL exceptions. This would get me to another objective of logging
every exception and logging each only once (not once in the DAL and then
again after rethrown to the PL).

So, what do you think? Am I totally nuts? Is this allright, or would my
approach amount to being a Rube Goldberg device?

Thanks!

I'd say that's totally fine.. In my view anything that reduces code
complexity is definately recomended.
As long as you can represent almost all the errors that could occur in the
DAL in a single (or maybe multiple) custom exceptions.

Simon.
 
D

Dave

Jeff S said:
Before I go off and paint myself into a corner by writing a bunch of code
that I later regret, I'd appreciate your feedback on my approach to
exception handling: My objective is this: in the DAL, if a runtime exception
occurs (e.g., can't connect to SQL Server), I want to log the details to an
application log, and then throw a custom exception up to the presentation
layer (Web UI in this case) that has a user-friendly message.

I defined the custom exception (which inherits from System.Exception of
course)

Best practices call for deriving it from ApplicationException; either will
work but this is the preferred approach.
as a public member of the DAL. The intent is to set the
user-friendly message in the DAL because that's where I know exactly what
went wrong and can translate that into what the user should do about it, if
anything.

Correct. I usually recommend the wrap-throw methodology, where you throw a
new exception (just as you propose) with a message that clearly describes
why the call failed, and also chain the original exception to the new
exception by setting it as the InnerException. For example...

throw new CustomApplicationException("A real friendly message
here",exRealExceptionObject);

This preserves the original exception (and stack trace) as well as providing
you a chance to add additional context information to aid the user in
determining what/where the problem is.
Separately, throwing a custom exception from the DAL (as opposed to simply
rethrowing the original exception) has the benefit that the presentation
layer (PL) can easily differentiate between different types of errors (those
originating in the DAL and those originating in the PL itself) without
having multipile try... catch blocks (would instead have multipile catch
blocks).

I'm not sure why you would need separate try-catch blocks just because you
rethrow the original exception. However, defining custom exceptions has
some benefits though I am less certain that they are really that terribly
useful when thrown across a machine (or component) boundary. You should
define a custom exception when you are adding new fields to the object or a
capability that the existing exception objects don't support, but this is
not as clear-cut as it sounds - in fact I consider this aspect of .net
programming to still be something of a black-art and is a matter of
considerable debate. This also doesn't address the issue of throwing
exceptions versus using sentinel return values to indicate an error.

If all the custom exception objects are used internally and do not propagate
outside of the boundaries of the component then there's little downside to
extending the hierarchy however you please, but if they do propagate outside
the component that generates them then this requires careful planning and
execution (read down for more..) I would not use a custom exception unless
there was a clear benefit to doing so.

Another reason to differentiate in the PL is that the PL in my case
would assume that any DAL exceptions have already been logged and would only
log non DAL exceptions. This would get me to another objective of logging
every exception and logging each only once (not once in the DAL and then
again after rethrown to the PL).

That's a nice touch but you may want to log it twice anyway. If the DAL is
on a server and the the UI is on another machine then it would be useful to
have both sides log the exception; this ensures that at least one record of
the failure exists (the SOAP reply packet may get lost, garbled, etc.). Also
you may only have access to one machine; e.g. an administrator may not be
present at or have access to the client site but can still view the error
log.
So, what do you think? Am I totally nuts? Is this allright, or would my
approach amount to being a Rube Goldberg device?
You're not nuts and it's not a hack; this is one of the recommended
procedures.

There are some additional constraints this approach requires that you should
be aware of. The most important is that the assembly that defines the custom
exception must be available on both the client and server machines,
otherwise the client will be unable to deserialize the exception object.

If the exception class is defined in an assembly with a strong name then
there are versioning issues that must be dealt with (e.g. the server throws
an exception defined in assembly 1.5 and the client has assembly 1.2). And
even if the assembly does not have a strong name to force strict binding on
you there are other reasons for making your exception object version
agnostic - you may add or remove fields and you absolutely, positively want
to be able to deserialize that object when it propagates across the wire to
your client, otherwise your nice shiny custom exception will be tossed and
instead you will be notified of a deserialization exception (note: this is
also a problem when throwing exceptions across any boundary, including
appdomains).

You should take a look at the MSFT exception management block; it may be a
good building block for you to start with.

I've condensed a list of DOs and DONTs for extending the exception hierarchy
that you may (or may not :)) find useful. I'll repeat them here (use at
your own risk of course and take it all with a dose of common sense). Many
are repeated from the MSFT guidelines.

DO's
1.. Do define your own exception class when appropriate. This occurs when
you expect class library users to perform a programmatic action based on the
exception type.
2.. Create a single base application class from which all your additional
specific exceptions are derived. This base class should have fields common
to all your exceptions, such as machine name, time-date stamp, etc.
1.. When dealing with multiple products there may be value in adding a
base class from which all components derive their own types. This would
require an assembly that needs to be deployed with all products.
3.. End exception class names with the suffix Exception.
4.. Provide three constructors for your exception class.
1.. XxxException() { . }
2.. XxxException(string message){ . }
3.. XxxException(string message, Exception inner){ . }
5.. All custom exception classes defined should be remotable (see below).
6.. The exception should be annotated with the [Serializable] attribute at
the class level.
7.. If extending the custom exception class with new fields you will need
to implement the ISerializable interface to allow it to be marshaled across
remote boundaries. You will need to add the custom constructor
protected XxxException(SerializationInfo info, StreamingContext context) :

base(info, context) { . }

and you will also need to persist the custom fields by overriding the
GetObjectData method, which has the following signature:

protected GetObjectData(SerializationInfo info, StreamingContext context)
{ . }

8.. All fields used to extend the exception class should be public so that
a single logging routine, which uses reflection to traverse public
properties, can extract and log all relevant data.
9.. Provide properties for programmatic access to extra information, but
only when there is a scenario where programmatic access is useful. Otherwise
include this extra information within the descriptive text.
10.. Override the ToString() implementation so that the extra fields you
added will be easily accessible.
Don'ts
1.. Don't extend classes directly from the base System.Exception class.
2.. Don't extend the hierarchy if the exception cannot be used to
programmatically recover from or perform a unique custom action based on the
exception type. In other words, do not do this just for informational
purposes - use the message field for that.
3.. Don't use exceptions for normal or expected errors - use a return
value or outbound argument for these.
Dave
 
J

Jeff S

Thanks Dave and Simon for the excellent feedback ... or shall I call it
*Exceptional* feedback (har har har!).

Since posting the request for a sanity check I have implemented the model as
described in the original post, and it gets me the behavior I was wanting.
I'm not going across machine or AppDomain boundaries, so that keeps things a
bit simpler.

One surprising discovery for me is that when I pass the various Exception
objects to an error logging routine (which writes details out to an XML
file), the StackTrace associated with the exception (e.g.,
SqlException.StackTrace) does not include the entire call stack (at least it
does not show all the modules/methods invoked from the start of the
application and leading up to the error). However, at the same time - the
same point in execution time - the Environment.StackTrace does contain the
entire call stack. Put another way, when the program is executing the
logging routine (in a static helper class), the two call stacks are not
equal. They are not equal in entries leading up to the point of the
exception, and of course only the Environment.StackTrace includes the call
to the error logging routine itself (but that's not a surprise).
Consequently in order to log everything I want to know about the StackTrace,
I am getting different pieces of information from the two different Stack
Traces. I'd be interested in knowing what the difference is because I
thought there was just one stack - and if there is, then any stacktrace
should show what's on it.

Thanks again.

Jeff
 
D

Dave

One surprising discovery for me is that when I pass the various Exception
objects to an error logging routine (which writes details out to an XML
file), the StackTrace associated with the exception (e.g.,
SqlException.StackTrace) does not include the entire call stack (at least it
does not show all the modules/methods invoked from the start of the
application and leading up to the error). However, at the same time - the
same point in execution time - the Environment.StackTrace does contain the
entire call stack.

This is the expected behavior. The exception object captures the stack from
the point where the exception occurred to where the exception was caught -
this is not the entire stack. Also, if you use the StackTrace property
directly you will get the stack trace for only that exception object, but
not for any innerExceptions. If you want a dump for all the chained
exceptions then user the exception.ToString() method (or run the chain
yourself).

If you call Environment.StackTrace in the catch handler you will see a
different stack due to the nature of how Structured Exception Handling (SEH)
works in .net. This uses a 2 pass mechanism when an exception occurs. The
1st pass locates a catch block for the exception, and the 2nd pass unwinds
stack frames, executing finally and fault blocks, up to the frame of the
catch handler. By the time the code in the catch handler executes the stack
has already been unwound from the point of the exception to the catch
handler. This means that if you capture the stack at that point you will
not see the frames that actually threw the exception - those frames have
been unwound (finally blocks executed) and popped off the stack.

Put another way, when the program is executing the
logging routine (in a static helper class), the two call stacks are not
equal. They are not equal in entries leading up to the point of the
exception, and of course only the Environment.StackTrace includes the call
to the error logging routine itself (but that's not a surprise).
Consequently in order to log everything I want to know about the StackTrace,
I am getting different pieces of information from the two different Stack
Traces. I'd be interested in knowing what the difference is because I
thought there was just one stack - and if there is, then any stacktrace
should show what's on it.

Basically, the exception object preserves the stack frames for reporting
purposes and then pops the stack; Environment.StackTrace reports on the
current stack but knows nothing about what happened before that point.

Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top