I've seen this pattern a few times now:
bool success = false;
try
{
DoSomething();
success = true;
}
finally
{
if (!success)
Rollback();
}
And I've been wondering: Why is this better than using catch for rollbacks?
try
{
DoSomething();
}
catch
{
Rollback();
throw;
}
What are the differences between the two ways of making sure changes are rolled back on failure?
The
finally
statement is usually used for cleaning up resources. TheRollback()
method might be okay to use there if exceptions are not the only reasons to roll back the transaction.Close()
orDispose()
methods are prime candidates to end up in the finally block.However, you do not want to execute anything there that can throw exceptions.
I'm not sure if this is not just anecdotal evidence, but I personally have used this pattern for a very practical reason: When
DoSomething
throws an exception, the Visual Studio debugger will break inDoSomething
where the exception occurs in the first version, while it will break at thethrow;
in the second version. This allows to inspect the application state beforeRollback
has cleaned everything up.I post here some code even if it's not really related to the question (will delete later).
With this program:
The stack trace (look at line numbers!) is preserved but inside the
main
function you'll see "line 19", the line wherethrow
is instead the true line wherefoo()
has been called (line 13).If you do not care in this particular code what type of exception you are catching use:
This will preserve Call Stack in its original form for 100%. Also if you use exception maching like this:
using finally at the end can make your code more readable than calling rollback from every single catch statement.
finally
is always executed, not only on catching exceptions.Granted, in this specific case the rollback is only needed when there was an error, but as a general pattern, the
try-finally
may be more useful for resource management (where often you need to ensure that you alwaysClose()
orDispose()
of your resource properly). Especially if the author of the code is coming from Java background where this idiom is more widespread.The clear goal here is to cause
Rollback
to be called in the event of any error. Both code snippets accomplish this goal. The first uses a finally, which always runs, that verifies that the last line of thetry
block was successfully reached. The second catches any errors, rolls back the transaction, and then re-throws the exception that was caught. The result of either snippet is that any exceptions thrown will result in a rollback while still bubbling up to the next level.You mentioned that the project was ported from Java. In Java, you can re-throw an exception similarly to how you can in C# using
throw;
. You can also throw a new exception that will still maintain the call stack (et al). The second is a bit clearer/simpler in C# (not by a lot though) and the first has the advantage of actually working in Java as written.