views:

141

answers:

7

Which is the best place to handle the exceptions ? BLL, DAL or PL ?

Should I allow the methods in the DAL and BLL to throw the exceptions up the chain and let the PL handle them? or should I handle them at the BLL ?

e.g

If I have a method in my DAL that issues "ExecuteNonQuery" and updates some records, and due to one or more reason, 0 rows are affected. Now, how should I let my PL know that whether an exception happened or there really was no rows matched to the condition. Should I use "try catch" in my PL code and let it know through an exception, or should I handle the exception at DAL and return some special code like (-1) to let the PL differentiate between the (exception) and (no rows matched condition i.e. zero rows affected) ?

+1  A: 

The layer that knows what to do to set things right should be the layer that handles the exception. For example, if you decide to handle deadlock errors by retrying the query a certain number of times, then you could build that into your DAL. If it continues to fail, then you might want to let the exception bubble up to the next layer, which can then decide if it knows how to properly handle this exception.

mbeckish
A: 

The question to you is where is the exception relevant? If it is a data access exception it should be caught in the DAL. If it is a logic exception it should be caught in the BLL. If it is a presentation exception then in the PL.

For instance if your DAL throws an exception it should return a null or a false or whatever the case may be to your BLL. Your BLL should know what to do if the DAL returns a null, maybe it passes it right through, maybe it tries calling another function, etc. The same goes with your PL if the BLL passes through a null from the DAL or returns something specific of its own then the presentation layer should be able to notify the end user that there was an issue.

Of course you won't get the verbose exception messages, but that is a good thing as far as your users are concerned. You should have a flexible logging system to catch these exceptions and report them to a database or an ip:port or whatever you decide.

Essentially you need to think in terms of separation of concerns if the concern is a data issue or a logic issue it should be handled accordingly.

joshlrogers
+1  A: 

The short answer is it depends!

You should only ever handle an exception if you can do something useful with it. The 'something useful' again depends on what you are doing. You may want to log the details of the exception although this isn't really handling it and you should really re-throw the exception after logging in most circumstances. You may want to wrap the exception in some other (possibly custom) exception in order to add more information to the exception. As @mbeckish touches on, you may want to try to recover from the exception by retrying the operation for example - you should be careful not to retry forever however. Finally (excuse the pun) you may want to use a finally block to clean up any resources such as an open DB connection. What you choose to do with the exception will influence where you handle it. It is likely that there isn't a great deal of useful things that can be done with many exceptions other than to report to the user that an error has occurred in which case it would be more than acceptable to handle the exception in the UI layer and report the problem to the user (you should probably log the exception as well, further down your layers).

When throwing exceptions yourself, you should only ever throw exceptions in 'exceptional'circumstances as there is a big overhead in throwing exceptions. In your example you suggest you may be thinking of throwing an exception if no records are updated by your operation. Is this really exceptional? A better thing to do in this situation would be to return the number of records updated - this may still be an error condition that needs to be reported to the user, but isn't exceptional like the command failing because the connecction to the DB has gone down.

This is a reasonable article on exception handling best practices.

s1mm0t
>...and you should really re-throw the exception after logging in most circumstancesI have to disagree with you there. Exception handling is an extremely expensive process and if you are doing things properly there should be very few cases in which you even have an exception thrown. When you start letting exceptions control the flow of your application you are opening yourself up for a lot of trouble. I would argue that you should NEVER re-throw an exception unless you have a VERY GOOD argument for doing so and even then the argument is probably not good enough.
joshlrogers
Sorry @joshrogers, I have to disagree strongly with you there. Catching an exception, logging it and then doing nothing is just plain wrong! I guess you mean that you should then return an error code as in you suggest in your answer. Personally I don't like this pattern. Although I agree you shouldn't use exceptions to control flow, the hit that you may get re-throwing an exception in this instance doesn't seem to be a big deal as ultimately something has gone wrong with the application. It also IMHO leads to cleaner code as you don't have to check return codes everywhere.
s1mm0t
What if I am logging my exceptions into some log source.e.g. If in one of a DAL method, I log my exception, and if that method is being called from several places in PL, how would the log convey the information that from where the exception occurred in the PL code ?
Puneet Dudeja
@Puneet Dudeja. You could either log the exception in your calling code, or the stack trace would tell you where the method that threw the exception, was called from.
s1mm0t
@s1mm0t I may be being misunderstood. I don't like return codes either unless you are handling some very specific cases and you have someone who is going/willing to diligently keep track of all potential error codes. Error codes are usually far too much overhead and far too "enterprisey" for me. Typically I go on the idea that if it is an object to be returned then a null is returned upon exception, if it is a bool then a false might get returned depending on the intended functionality, if it is a value type then I usually will re throw the exception to be handled by the calling method.
joshlrogers
@joshlrogers Just want to discuss that, will it not be better that even if a method returns a value type, we change that method to return a reference type and use boxing to return the value type, so that upon exception we can still return a NULL. This will also maintain uniformity in exceptional handling code ?
Puneet Dudeja
@Puneet Dudeja I don't know how much you want to follow my advice as I seem to be the minority here and that may be for a reason. However, my opinion is no because boxing can be a fairly heavy process as well. You will have far more successes than you would failures, or at least you should, so the boxing and unboxing will be more expensive than just re-throwing an exception every now and then. I find it more beneficial to handle the exception in the calling layer when you are dealing with value type. Plus I think <[insert type here>variable sprinkled all over your code is not very elegant.
joshlrogers
@joshlrogers. Taking just one of your examples, although I think this holds true for all of them, in the case of returning NULL when an exeception occurs, this again I feel is incorrect. Consider a method that reads a record from the database. If the record doesn't exists, this is probably not an 'exceptional' condition and therefore NULL should be returned. This is quite different from when this method is called and the connection goes down - in this case NULL shouldn't be returned, an exception should be thrown.
s1mm0t
@s1mm0t I agree that an exception should be thrown but why does the BL have to be aware of the exception? The end result is the same and that is that there are no records to work on which means the PL doesn't have anything to display. If you do not have any type of logging then yes you pretty much need this to bubble up so that someone is notified but if you are adequately logging your exceptions then the exception is saved to a db/file/etc. and the user isn't attacked with an exception message. I have yet to find an argument to make my BL concerned with handling SQLExceptions or the like.
joshlrogers
@s1mm0t I am an advocate of keeping layers agnostic. Layer's should only know enough about one another to be able to function. Each layer should do its best to handle its own issues and cleanly notify, if needed, the calling layer. I know it is a preference thing, but I just don't believe that my BL or my PL should be concerned with what exceptions were thrown by the other layer just that it failed and then act accordingly from there. If my BL has to be aware of all the different exceptions that my DAL could throw, that is far too much mixing of concerns for me.
joshlrogers
+1  A: 

This is a huge topic with lots of unneeded controversy (people with loud voices giving bad info!) If you're willing to deal with that, follow s1mm0t's advice, it is mostly agreeable.

However if you want a one-word answer, put them in the PL. Serious. If you can get away with it put your error handling in a global exception handler (all errors should log and give a code to look up the log in production for security reasons (esp if web), but give the full details back during development for speed reasons).

Edit: (clarification) you have to deal with some errors everywhere - but this is not an 'every function' norm. Most of the time let them bubble up to the PL and handle .NET's global error with your own code: log the full call stack from there via a common routine that is accessible from all 3 layers via event handlers (see EDIT at bottom of message). This means you will not have try/catch sprinkled thru all your code; just sections you expect and error and can handle it right there, or, non-critical sections, with which you log the error and inform the user of unavailable functionality (this is even more rare and for super-reliable/critical programs)

Aside from that, when working with limited-resource items, I often use the 'using' keyword or try/finally/end try without the catch. for multithreading lock/mutex/re-entry prevention flags/etc. you also need try/finally in ALL cases so your program still works (especially stateful apps).

If you're using exceptions improperly (e.g., to deal with non-bugs when you should be using the IF statement or checking it an iffy operation will work before you try it), this philosophy will fall apart more.

A side note, in thick client apps especially when there is the possibility of losing significant amounts or the users' input, you may be better with more try/catches where you attempt to save the data (marked as not-yet-valid of course).

EDIT: another need for at least having the logging routine in the PL - this will work differently depending on the platform. An app we're working on shares the BLL/DAL with 3 PL versions: an ASP.Net version, a winforms version, and a console app batch mode regression testing version. The logging routine that is called is actually in the BLL (the DAL only throws errors or totally handles any it gets or re-throws them). However this raises an event that is handled by the PL; on the web it puts it in the server's log and does web-style error message display (friendly message for production); in WinForms a special message window appears with tech support info, etc. and logs the error behind the scenes (developers can do something 'secret' to see the full info). And of course in the testing version it is a much simpler process but different as well.
Not sure how I'd have done that in the BLL except for passing a parameter 'what platform,' but since it doesn't include winforms or asp libraries that the logging depends on, that still would be a trick.

FastAl
In one of my projects, I handled all exceptions at the PL and logged them in the PL only. But that makes the PL code very cluttered and the real logic unreadable. That is also one of the reason, I want to log the exceptions at the lower layers.
Puneet Dudeja
If you're using winforms or asp.net, you don't have to clutter up the PL with handlers. Use the global exception handles that those frameworks provide. You set it up in the global asp or sub main, then, boom, every hit/UI action gets its own 'try catch' before entering your code, and all errors go to one place in your code. It's awesome! Yes, there are drawbacks and limitations, but they are not insurmountable.
FastAl
A: 

All layers in your application should manage exceptions gracefullly. This is know as a cross cutting corncern, because it appears in all your layers. I belive that using a framework like Enterprise Exception Block with unity, you will end up with a better code overall. Take a look at this post

http://msdn.microsoft.com/en-us/library/ff664698(v=PandP.50).aspx

It will take sometime to master it, but there are lots of examples and screencast around there.

+2  A: 

It makes no sense to let an exception that is thrown in the DAL bubble up to the PL - how is the user supposed to react if the database connection could not be established?

Catch and handle exceptions early, if you can handle them. Do not just swallow them without outputting a hint or a log message - this will lead to severe difficulties and bugs that are hard to track.

Marius Schulz
Be prepared to be downvoted with me...this strategy is not popular today it seems :)
joshlrogers
+1, your answer was perfectly okay and even more detailed than mine.
Marius Schulz
I'm not going to downvote you ... BUT... -some errors are OK to handle in lower levels. It will not be a hard and fast rule but a mindset. But no DB connection? The PL has to know, even if you change the message.-about not swallowing them - AMEN! NEVER should anybody do that. Best practices also require always logging ex.tostring never just message. And having the PDB w/ line #s (even if this is hard and you need to fight security/political/technical worries). Therefore a complete stacktrace in the log. Different machines? Still have to retain innerexceptions, it's possible.
FastAl
@FastAl Why does the PL have to know if the DAL has no DB connection? If you have adequate logging why can't the PL just get receive null or false and it either displays nothing other than a message about not being able to retrieve the requested data. It's non consequential to the user that the DB connection is down and if recurring could lead to lack of confidence. If the users know there should be something returned they will let you know anyways, plus you'll have an error log of it. BTW I store the state of the class and arguments in my log so I can reproduce easily using reflection.
joshlrogers
@FastAl: See joshlrogers' answer - he explained what I meaned ...
Marius Schulz
@joshlrogers I just don't see why you don't want to be able to differentiate between an error condition and no data being returned. I think we will have to agree to disagree.
s1mm0t
@s1mm0t: He did not say that he does not want to be able to differantiate between error conditions and missing data! He only proposed to notify the user if the database connection could not be established as the user himself is unable to do anything if there really is no connection ...
Marius Schulz
FastAl
A: 

How to handle exceptions depends on technical and business needs. For complex or highly important database updates I include out params that pass a small list of known errors backup to the DL. This way, known error scenarios can be programmatically solved in some cases. In other cases the error needs to be logged and the user should be notified of an error.

I make a practice of notifying a human being of errors. Sure, logging will give us detailed information, but it's no replacement for the response time of a human being. Not only that, but why force developers to watch system logs just to see if things are going south? Talk about unnecessary cost.

If you have time to define potential errors/exceptions and programmatically solve them, then by all means do it. Many times errors/exceptions are unexpected. That's why it is important to be prepared for that unexpected and what better way to do that than involving a human being.

Overall, one should be on the defensive when planning exception handling. Programs grow or they die. A part of growing is introducing bugs. So don't spin your wheels trying to kill them all.

P.Brian.Mackey