3

I've decided to remove some of the using statements in my code so I can catch specific exceptions and handle the disposing of resources manually. I have refactored some of the code to make it more readable and maintable, after implementing the new try/catch block I am left wondering if they have been placed correctly for the task at hand.

Example:

public static DataTable Select(string table, string[] column, Object operand)
{
        DataTable dTable = null;
        SQLiteConnection connection = null;
        SQLiteCommand command = null;
        SQLiteDataReader dReader = null;

        //convert to array for arguments
        StringBuilder query = new StringBuilder();
        query.Append("select ");

        for (int i = 0; i < column.Length; i++)
        {
            query.Append(column[i]);

            if (i < column.Length - 1)
            {
                query.Append(",");
            }
        }
        query.Append(" from ");
        query.Append(table);

        try
        {
            connection = new SQLiteConnection(_connectionString);
            command = new SQLiteCommand(query.ToString(), connection);
            dTable = new DataTable();

            connection.Open();

            dReader = command.ExecuteReader();

            dTable.Load(dReader);

            return dTable;
        }
        catch (SQLiteException sqle)
        {
            //Handle exception
        }
        finally
        {
            connection.Dispose();
            command.Dispose();
            dReader.Dispose();
            dTable.Dispose();
        }
        return null;
}

In this example I have only implemented the try/catch around the SQL operations themselves, I did this as it ensures any exceptions that are thrown can be noted and the resourced disposed correctly. I then noticed that this left the for loop open to exceptions, although the supplied indexer will be protected and created via a GUI.

Would I be wise to encapsulate the entire method in the try/catch statement or am I being overly cautious? You could say I'm looking for the best practice when it comes to managing the placement of the statements themselves.

Thanks for your time!

Edit:

I know that the using statement would be ideal in terms of handling the disposal and management of resources however as mentioned at the beginning of the question I wish to be able to catch specific types of exceptions, in particular those generated from the SQLite components.

Jamie Keeling
  • 9,806
  • 17
  • 65
  • 102
  • Side note: you're using string concatenation to build your query, so be careful of SQL injection attacks here. It might not be obvious outside of this method that arguments being passed into it need to be checked for problems ahead of time. Intuitively, I would expect the data access class to handle that, but this one isn't. – David Apr 03 '11 at 20:37
  • I'm using parameterised queries in all of my methods, the columns are generated by the GUI and cannot be manually entered via user input. Thanks for the pointers though :) – Jamie Keeling Apr 03 '11 at 21:48

6 Answers6

9

Don't forget a null check:

finally {
  if (connection != null) connection.Dispose();
  if (command != null) command.Dispose();
  if (dReader != null) dReader.Dispose();
  if (dTable != null) dTable.Dispose();
}

It is possible that one of the constructors throws an exception, in that case the objects will not have been initialized.

Jan
  • 8,011
  • 3
  • 38
  • 60
3

Why use try/catch explicitly when what you are concerned about is resource management? Use using instead:

using(SQLiteConnection connection = new SQLiteConnection(_connectionString))
{ 
   ..
   using(SQLiteCommand command = new SQLiteCommand(query.ToString(), connection))
   {
      using(SQLiteDataReader  reader = dReader = command.ExecuteReader())
      {
          dTable.Load(dReader);
      }
   }
}

Also currently you are returning dTable, but you are disposing it in your finally block.

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • 1
    Kind of silly for you to explain how to use `using` statements when he said he used to use them, but removed them. He also gave his reasons for removing the `using` statements. Did you actually read his post? – Erik Funkenbusch Apr 03 '11 at 21:06
  • I guess I did overlook that sentence - still it doesn't make sense to me - try/catch does not prohibit you from using `using` statements, you can do both. – BrokenGlass Apr 03 '11 at 21:16
  • 1
    You need a `using` statement when you have a try-finally scenario. If you have a try-catch-finally scenario I think it is perfectly valid to not use `using` but use the structures you have to put there anyway. – Jan Apr 03 '11 at 21:32
  • @Jan: That's where we disagree then - in the same scenario I would just wrap the using blocks with a try/catch block, not saying your approach is not valid, it's just more error-prone imo – BrokenGlass Apr 03 '11 at 21:44
  • @BrokenGlass In what way is it more error prone? The using statement equates to a try-catch statement, with it implemented in it's current form I can perform operations on the specific exceptions that can arise. I've stated at the beginning of the question that I do not wish to use the using statement as I want to be able to catch specific exception types and perform different operations accordingly. – Jamie Keeling Apr 03 '11 at 21:53
  • @Jamie: using is equivalent to try/finally - no catch. You can still wrap it with a try/catch to handle exceptions w/o having to worry about disposing resources manually. – BrokenGlass Apr 03 '11 at 21:58
  • I understand, however I feel that having an additional try clause for the sake of having managed resource disposal isn't worth the additional overhead (I assume there would be to some degree) as opposed to a single try-catch-finally that can catch the exceptions I need as well as dispose of the resources I choose. – Jamie Keeling Apr 03 '11 at 22:01
  • @Jamie: The overhead of a try/finally block is very tiny in the overall scheme of things, also see this related SO answer in regards to the generated IL: http://stackoverflow.com/questions/4106891/overhead-of-try-finally-in-c I do respect your choice, just giving you the information. – BrokenGlass Apr 03 '11 at 22:05
  • Thanks for the information, I really appreciate your input and i'll definitely take it on board. It's just a shame that I need the specific exceptions caught otherwise the using statement would definitely be the answer. – Jamie Keeling Apr 03 '11 at 22:11
2

You could enclose the entire method in a try/catch if you think that there's any likelihood that exceptions will be thrown outside of the scope of database problems. You can easily differentiate the exceptions by what you catch. For example:

DataTable dTable = null;
SQLiteConnection connection = null;
SQLiteCommand command = null;
SQLiteDataReader dReader = null;

try
{
  // non-DB code

  // DB code
}
catch (SQLiteException sqle)
{
  // Handle DB exception
}
catch (IndexOutOfRangeException ie)
{
  // If you think there might be a problem with index range in the loop, for example
}
catch (Exception ex)
{
  // If you want to catch any exception that the previous catches don't catch (that is, if you want to handle other exceptions, rather than let them bubble up to the method caller)
}
finally
{
  // I recommend doing some null-checking here, otherwise you risk a NullReferenceException.  There's nothing quite like throwing an exception from within a finally block for fun debugging.
  connection.Dispose();
  command.Dispose();
  dReader.Dispose();
  dTable.Dispose();
}
return null;
David
  • 208,112
  • 36
  • 198
  • 279
1

A bigger problem you've introduced with this refactoring can be seen when you consider what will happen when the creation of one of the intermediate entities fails. For example, if the connection creation throws, then what happens to the exception thrown in your finally block that attemts to call Dispose on all those null variables? At least check for null beforehand, or put an additional try/catch inside your finally. I'd say you could benefit from the use of Resharper, which would have already pointed out these issues.

Brian Kretzler
  • 9,748
  • 1
  • 31
  • 28
1

I don't know if it is best practice but I usually put try/catch statements around blocks of code where external resources are accessed like database access, file I/O, etc. which might spit out exceptions due to various reason (inaccessible resource, I/O error, etc.). I don't protect code which I'm in control of - that's where unit tests come into place

Btw: You know that you can replace your loop with string.Join()?

Update: Just to clarify: try/catch blocks only really make sense if you want to catch specific exception and execute some custom logic. Otherwise you should stick to using and let the exception bubble up and deal with it at the appropriate place (e.g. notify the user that some data could not be saved because the server is unavailable or so)

ChrisWue
  • 18,612
  • 4
  • 58
  • 83
0

If all you are worried about is properly disposing of things, you should be using a 'using' block for this. http://msdn.microsoft.com/en-us/library/yh598w02.aspx

Robert Levy
  • 28,747
  • 6
  • 62
  • 94