It is technically costly to throw new exceptions, however I won't make a big debate out of that since "costly" is relative - if you're throwing 100 such exceptions a minute, you will likely not see the cost; if you're throwing 1000 such exceptions a second, you very well may see a performance hit (hence, not really worth discussing here - performance is your call).
I guess I have to ask why this approach is being used. Is it really true that you can add meaningful exception information at every level where an exception might be thrown and, if so, is it also true that the information will be:
- Something you actually want to share with your user?
- Something your user will be able to interpret, understand and use?
- Written in such a way that it will not interfere with later reuse of low-level components, the utility of which might not be known when they were written?
I ask about sharing information with your user because, in your example, your artificial stack starts by informing the user there was a problem authenticating on the database. For a potential hacker, that's a good piece of information that exposes something about what the operation was doing.
As for handing back an entire custom exception stack, I don't think it's something that will be useful to most (honest) users. If I'm having trouble getting a list of customer names, for instance, is it going to help me (as a user) to know there was a problem authenticating with the database? Unless you're using integrated authentication, and each of your users has an account, and the ability to contact a system administrator to find out why their account lacks privileges, probably not.
I would begin by first deciding if there is really a semantic difference between the Framework exception thrown and the exception message you'd like to provide to the user. If there is, then go ahead and use a custom exception at the lowest level ('login failed' in your example). The steps following that, up to the actual presentation of the exception, don't really require any custom exceptions. The exception you're interested in has already been generated (the login has failed) - continuing to wrap that message at every level of the call stack serves no real purpose other than exposing your call stack to your users. For those "middle" steps, assuming any try/catch blocks are in place, a simple 'log and throw' strategy would work fine.
Really, though, this strategy has another potential flaw: it forces upon the developer the responsibility for maintaining the custom exception standard that's been implemented. Since you can't possibly know every permutation of call hierarchy when writing low-level types (their "clients" might not even have been written yet), it seems unlikely that all developers - or even one developer - would remember to wrap and customize any error condition in every code block.
Instead of working from the bottom up, I typically worry about the display of thrown exceptions as late in the process as possible (i.e. as close to the "top" of the call stack as possible). Normally, I don't try to replace any messages in exceptions thrown at low levels of my applications - particularly since the usage of those low level members tend to get more and more abstract the deeper the call gets. I tend to catch and log exceptions in the business tier and lower, then deal with displaying them in a comprehensible manner in the presentation tier.
Here are a couple of decent articles on exception handling best practices:
http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx
http://aspalliance.com/1119
Jeez this got wordy...apologies in advance.