开发者

Where should I handle the exceptions, in the BLL, DAL or PL?

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 wa开发者_运维百科s 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) ?


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.


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.


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.


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.


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.


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜