2

Is it possible for the Dispose method of a SqlConnnection object to throw an exception? It's always shown sitting on its own in a finally block outside of the try-catch. And if a using block is equivalent to a try...finally which then calls Dispose, that would also seem to be a case when an exception thrown by Dispose would be problematic.

notnot
  • 4,472
  • 12
  • 46
  • 57
  • I think any method can throw an exception. You can place and try catch in the finally. – paparazzo Dec 15 '14 at 16:56
  • This may be useful http://stackoverflow.com/questions/1030455/how-to-handle-exception-thrown-from-dispose – grovesNL Dec 15 '14 at 16:57
  • `SqlConnection` (almost) certainly will throw an exception if you try to call `Dispose()` twice in a row (on the second time). – EkoostikMartin Dec 15 '14 at 17:00
  • 1
    @EkoostikMartin Will it? I believe all `Dispose` does is close the unmanaged connection - why would trying to close it again cause an exception? – D Stanley Dec 15 '14 at 17:06

2 Answers2

5

Technically it can, but it shouldn't:

CA1065: Do not raise exceptions in unexpected locations :

A IDisposable.Dispose method should not throw an exception. Dispose is often called as part of the clean up logic in a finally clause. Therefore, explicitly throwing an exception from Dispose forces the user to add exception handling inside the finally clause. The Dispose(false) code path should never throw exceptions, because this is almost always called from a finalizer.

Dispose Dos and Don'ts:

Don't throw exceptions in Dispose. Nothing should go wrong with your object calling Dispose.

The moral of the story appears to be that an exception thrown from Dispose is a very bad thing (for some of the reasons you mentioned) and should be handled as high as possible (there's nothing you can do about it, and probably can't recover from it).

D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • I guess all the .NET libraries are rock-solid about it not throwing an exception, and that it only happens if user-defined or third party code introduces a problem? – Panzercrisis Dec 15 '14 at 17:50
  • 2
    That may be generalizing too much - I'm sure there are conditions where the .NET libraries can throw an exception, but what are you going to do about it? Unless you can do something different to handle it there's not much point in catching the exception. You probably can't just continue on as though nothing has gone wrong (at least not at that level). – D Stanley Dec 15 '14 at 20:07
1

In general, a block of code should finish normally if the state of the system will match what the surrounding code expects, and throw an exception if it won't. The the try/catch/finally construct in .NET has a weakness, however, which flows into the IDisposable interface which is designed to work with it (via language constructs like using): a method

void Test()
{
    try
    {
      doSomething();
    }
    finally
    {
      doCleanup();
    }
}

should only exit normally if both doSomething() and doCleanup() succeed, but if doSomething() throws an exception, the Test() method as a whole should either throw that exception or make it possible to log it. Meeting both requirements would require either that doCleanup() throw an exception of it runs following a successful call to doSomething(), but not following a call which threw an exception, or else that any exception it throws incorporates information about the exception thrown from doSomething().

Unfortunately, there is no convenient way of writing a finally block to vary its behavior based upon the result of the try , nor is there any way for a Dispose() method invoked from a finally to do so. Thus, it is necessary for Dispose implementations to try to guess whether it's more evil to exit normally when an operation fails, or throw an exception which may overwrite an earlier one that held useful information. The latter is somewhat evil, but often not as evil as the former.

For example, consider the pseudocode:

RenameFile(mainFileName, backupFileName);
using(outFile = File.Create(mainFileName);
{
  writeDataToNewFile(outFile);
}
DeleteFile(backupFileName);

If something goes wrong while closing the output file and the exception gets propagated out of the using block, the backup file will be left intact and might be recovered later. If the using block were allowed to complete normally despite a failure which prevented the main file from being properly written, the backup file would get deleted, possibly destroying the only copy of some of the data contained therein.

The idea that Dispose shouldn't throw exceptions relates to the fact that there's often no good way to handle exceptions thrown from Dispose. The bigger problem, however, is that there's no good way of handling problems that arise during Dispose without knowing the circumstances under which it is invoked. Throwing an exception from Dispose may be somewhat evil, but still be less evil than allowing it to complete normally in cases where real problems exist.

supercat
  • 77,689
  • 9
  • 166
  • 211