11

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?

configurator
  • 40,828
  • 14
  • 81
  • 115

6 Answers6

4

I post here some code even if it's not really related to the question (will delete later).

With this program:

using System;

namespace testcs
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                try
                {
                    foo();
                    foo();
                    foo();
                }
                catch
                {
                    throw;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void foo()
        {
            throw new Exception("oops");
        }
    }
}

The stack trace (look at line numbers!) is preserved but inside the main function you'll see "line 19", the line where throw is instead the true line where foo() has been called (line 13).

Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
  • 3
    +1, point taken, thanks for taking the time to create the sample. I don't think you necessarily need to delete it, since it illustrates the difference between the two methods in the question (and it's definitely too long for a comment). – Heinzi May 23 '12 at 14:36
3

The finally statement is usually used for cleaning up resources. The Rollback() method might be okay to use there if exceptions are not the only reasons to roll back the transaction. Close() or Dispose() methods are prime candidates to end up in the finally block.

However, you do not want to execute anything there that can throw exceptions.

Dmitry S.
  • 8,373
  • 2
  • 39
  • 49
  • It is simply common to do resource cleanup in the finally statement, as I mentioned. This way you do not have to rethrow. – Dmitry S. May 23 '12 at 14:26
  • @configurator funny, it is the highest rated so far, and there are others similar to it, maybe edit the question to elicit responses more in line with your desire? – Joshua Drake May 23 '12 at 14:33
2

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 in DoSomething where the exception occurs in the first version, while it will break at the throw; in the second version. This allows to inspect the application state before Rollback has cleaned everything up.

Screenshot

dtb
  • 213,145
  • 36
  • 401
  • 431
2

If you do not care in this particular code what type of exception you are catching use:

try
{
   DoSomething();
   ok = true;
}
finally
{
    if(!ok)Rollback();
}

This will preserve Call Stack in its original form for 100%. Also if you use exception maching like this:

try
{
   DoSomething();
   ok = true;
}
catch(FirstExcetionType e1)
{
    //do something
}
catch(SecondExcetionType e2)
{
    //do something
}
catch(Exception e3)
{
    //do something
}
finally
{
    if(!ok)Rollback();
}

using finally at the end can make your code more readable than calling rollback from every single catch statement.

Grzegorz W
  • 3,487
  • 1
  • 21
  • 21
1

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 always Close() or Dispose() of your resource properly). Especially if the author of the code is coming from Java background where this idiom is more widespread.

Péter Török
  • 114,404
  • 31
  • 268
  • 329
0

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 the try 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.

Community
  • 1
  • 1
Servy
  • 202,030
  • 26
  • 332
  • 449
  • This makes sense. I didn't realize Java doesn't allow you to properly rethrow excpetions. And here I was thinking this was going to have something to do with critical sections and the likes... – configurator May 23 '12 at 15:50
  • @configurator: Now that you mention it, I think there may indeed be a connection to [Constrained Execution Regions](http://msdn.microsoft.com/en-us/library/ms228973.aspx). – dtb May 23 '12 at 15:58
  • 4
    -1 for "In Java there is no way to re-throw an exception" - [apart from `throw e`, that is](http://stackoverflow.com/a/1097539/265143). – Péter Török May 24 '12 at 07:38