Let\'s assume there is an operation that creates a user. This operation may fail if specified email or username exists. If it has failed, it is required to know exactly why. The
You specified that UserExists
and UsernameExists
both make DB calls. I'll assume that CreateUser
also makes a database call.
Why not let the database handle your threading issues? Assuming further that there are proper constraints set up on your table(s), you can let it err-out within a stored proc call.
Therefore, I'll cast my vote for Create-Catch.
So, do we have a clear winner or it's more a matter of taste?
The first option has a fundamental flaw - it's never going to be thread safe or safe if CreateUser
relies on external resources, and other implementations may create in between your tests. In general, I tend to avoid this "pattern" because of this.
As for the other two options - it really comes down to whether the failure is expected to happen. If CreateUser
would be expected to fail on a somewhat normal basis, the Try* pattern is my preference, as using exceptions essentially becomes using exceptions for control flow.
If the failure would truly be an exceptional case, then exceptions would be more understandable.
Test-Create can cause race conditions, so it's not a great idea. It also likely does extra work.
Try-Create is good if you expect errors to be part of normal code flow (such as in the case of user input).
Create-Catch is good if errors are truly exceptional (so you're not worried about performance).
This is somewhat subjective, but there are some concrete pros and cons that are worth pointing out.
One drawback of the test-create approach is the race condition. If two clients attempt to create the same user at approximately the same time, it may be possible for them to both pass the tests, and then attempt to create same user.
Between Try-Create and Create-Catch, I prefer Create-Catch, but that's personal taste. One could argue that Create-Catch uses exceptions for flow control, which is usually frowned upon. On the other hand, Try-Create requires a somewhat awkward output
parameter, which could be more easily overlooked.
So, I prefer Create-Catch, but there's definitely room for debate here.