I need help on on n-tier layer programming (DAL, BLL, WEB) and exceptions

sao123

Lifer
May 27, 2002
12,653
205
106
So I am working on an ASP.net website software project for work.

In my VS 2012 solution, there are 3 separate projects for the N-tier layers.

we have the Data Access Layer (DAL), which is pretty much all the SQL data models and the ADO.net code to read and write to SQL Server through Stored procedures.

Then I have a Business Logic Layer, which does all the business decisions.

Finally I have the ASPX Webforms project which contains all the webpages and their respective code-behind.


What I need to make some decisions on is the best way to "bubble" my exceptions up through the layers. My goal is to log (log4net) the real exceptions at the lowest layer possible, then create my own custom wrapper exception classes, to pass up to the next layer, and finally up to the web layer for the purpose of displaying generic-but user friendly error messages.

my main point of concern is including the original exception as part of my custom exception as the inner-exception, so if I need those details in a higher layer they are available.

So lets say I get a SQLExceptionError in the DAL, and wrap it in my own DatabaseFailureException(FriendlyMsg, OriginalExceptionAsInnerException) and throw that to the Business Logic Layer. i catch it there.

Now here is my problem... do I catch that Exception and just rethrow it up to the web layer? Or should I wrap it in another wrapper exception and then throw it?

I was doing some reading, and I don't quite understand everything... However the general consensus is that if not done correctly... the InnerException gets lost OR you lost the complete Stack Trace when rethrowing exceptions through n-tier layers

Can someone explain this problem in further depth to me and then explain how can I correctly bubble up my exceptions through 3 layers but still have the relevant Stack Trace and InnerException across all 3 of my layers?
 

KB

Diamond Member
Nov 8, 1999
5,406
389
126
You are on the right track. Catch the ADO.Net Exception, pass it in a constructor to a more friendly DatabaseErrorMessageException class. This class converts the database error messages like "contraint IX_Employee violation" to "That employee number is already in use" sets the SQLException to be its InnerExcemption and gets thrown up the chain to the Business Layer. I think this one wrapper exception class is enough since the error message at this point is good enough to show the user, but in you logging code you would show the whole stacktrace and the stacktrace for any InnerException if it exists.
 

sao123

Lifer
May 27, 2002
12,653
205
106
You are on the right track. Catch the ADO.Net Exception, pass it in a constructor to a more friendly DatabaseErrorMessageException class. This class converts the database error messages like "contraint IX_Employee violation" to "That employee number is already in use" sets the SQLException to be its InnerExcemption and gets thrown up the chain to the Business Layer. I think this one wrapper exception class is enough since the error message at this point is good enough to show the user, but in you logging code you would show the whole stacktrace and the stacktrace for any InnerException if it exists.

So in the BLL, I need to catch and throw it up to the web layer to do a page redirect...

Is this sufficient? (it doesn't appear C# has the rethrow that java supports.

catch (Exception ex)
{
throw Ex; //Don't do throw NEW
}
 

beginner99

Diamond Member
Jun 2, 2009
5,312
1,750
136
My goal is to log (log4net) the real exceptions at the lowest layer possible, then create my own custom wrapper exception classes, to pass up to the next layer, and finally up to the web layer for the purpose of displaying generic-but user friendly error messages.

You should never log an exception and then rethrow it. You either deal with it and log it or you ignore it and let the next layer deal with it. Anything else is a a huge Anti-pattern. Why? It most likely will get logged twice.

I'm also not sure wrapping it is a good idea (in .NET, makes more sense in Java with stupid checked exceptions). General pattern is if you can't deal with it (eg. present error to user) then the let the next layer deal with it. Wrapping it will only create tons of not very useful boilerplate code. You can just as well create the friendly error message at any layer. Important part is that the log contains relevant information. Because that's were the developer will look in for fixing bugs.

If businesslayer calls addUser() method and error is "duplicate primary key" the business-layer can easily deal with this exception. No need to do it in the dal. Actually there is hardly a reason to catch anything in the DAL. Above example is also dumb because stuff like that should be validated before insert anyway.
 

KB

Diamond Member
Nov 8, 1999
5,406
389
126
So in the BLL, I need to catch and throw it up to the web layer to do a page redirect...

Is this sufficient? (it doesn't appear C# has the rethrow that java supports.

catch (Exception ex)
{
throw Ex; //Don't do throw NEW
}

I typically don't catch the generic exceptions in the DAL. Things like memory errors should be allowed to bubble-up to the businesslayer since it is unexpected and should be logged as unexpected.

In the DAL I will catch more expected errors like index violations, disconnected status


Code:
catch(SqlException exsql){
   if(exsql.Message.indexOf('IX_Employee') > -1) {
      throw new DatabaseErrorException(exsql, "The employee number is already in use");
 } elseif(exsql.Message.indexOf('login failed') > -1){
 throw new DatabaseErrorException(exsql, "Username/password is incorrect. Please verify and try again");
}
  //throw unexpected sql exception
  throw new Exception(exsql.Message);
}

//allow generic exception to bubble
 

sao123

Lifer
May 27, 2002
12,653
205
106
You should never log an exception and then rethrow it. You either deal with it and log it or you ignore it and let the next layer deal with it. Anything else is a a huge Anti-pattern. Why? It most likely will get logged twice.

I'm also not sure wrapping it is a good idea (in .NET, makes more sense in Java with stupid checked exceptions). General pattern is if you can't deal with it (eg. present error to user) then the let the next layer deal with it. Wrapping it will only create tons of not very useful boilerplate code. You can just as well create the friendly error message at any layer. Important part is that the log contains relevant information. Because that's were the developer will look in for fixing bugs.

If businesslayer calls addUser() method and error is "duplicate primary key" the business-layer can easily deal with this exception. No need to do it in the dal. Actually there is hardly a reason to catch anything in the DAL. Above example is also dumb because stuff like that should be validated before insert anyway.


Well this brings up many problems then in need of a solution... many times "Handling" requires something to happen in multiple layers.

in the DAL, the exception might be SQLException, in this case the DBConnection must be closed() in the DAL... this cant be done in the BLL, The DBConnection is out of scope.

Then the DAL must inform the BLL that an exception has occurred, and not simply return 0 rows modified. (Re-throw to the next level).

The BLL catches and handles the exception according to business rules, then the exception must be passed up to the web layer to Response.Redirect the user to an error page.

The layers are in physical separate projects/DLL's, so I cant just page redirect from the BLL. So I am wrapping it in my own exception class and passing it as the inner exception.