1

Okay, I have some very simple code which I will post below. Essentially, I have a connection to a database and I want to map a subset of columns in a query to a particular class. The problem is that it is possible for these to be null.

I would like to know if it is possible if an exception is thrown at a particular line, can we resume the entire block from the next line.

So if this code below was to execute and Line 6 catches an error. Is there an elegant way to catch the exception and make the code resume running at line 7. Essentially making it as though line 6 was never executed.

 private static Column MapTableToColumn(OracleDataReader reader){
        Column c = new Column();
        c.ColumnName = Convert.ToString(reader["COLUMN_NAME"]);
        c.DataType = Convert.ToString(reader["DATA_TYPE"]);
        c.DataLength = Convert.ToInt32(reader["DATA_LENGTH"]);
        c.DataPrecision  = Convert.ToInt32(reader["Data_Precision"]);//<---Line 6
        c.DataScale = Convert.ToInt32(reader["Data_scale"]);//<--- Line 7
        c.AllowDBNull = Convert.ToBoolean(reader["ALLOW_DB_NULL"]);
        c.IsReadOnly = Convert.ToBoolean(reader["IS_READ_ONLY"]);
        c.IsLong = Convert.ToBoolean(reader["IS_LONG"]);
        c.IsKey = Convert.ToBoolean(reader["IS_KEY"]);
        c.KeyType = Convert.ToString(reader["KEY_TYPE"]);
        c.IsUnique = Convert.ToBoolean(reader["IS_UNIQUE"]);
        c.Description = Convert.ToString(reader["DESCRIPTION"]);
        return c;
    }

It is important to note I am not asking for best practice, it is not something I intend to use in actual code (unless its absolutely genius). I simply want to know if this is possible and how one would go about doing this if it were.

My Research

Most of my research is proactive as opposed to reactive. I would attempt to know if it is possible for the given field to be null before it is read from. If it is, then I'd do a check to determine if the field is null and then set it to a default value. It essentially avoids the possibility of an error happening which I believe is a very good solution. I just wanted to attempt this as I know that when an exception is thrown, the most inner exception contains the line number at which it was thrown. Based on this if you put the exception inside of the class throwing the exception you should hypothetically be able to use reflection in order to continue running from its last point. I'm just not sure how you'd go about doing this. I've also considered the possibly of putting try catches around every single line which I think would be very effective; however, I think that it would be very ugly.

Aelphaeis
  • 2,593
  • 3
  • 24
  • 42

4 Answers4

8

No, what you are asking for is not possible in C#.

Instead the proper solution to this problem is to use better parsing methods that won't throw exceptions in the first place. If your input values can be null, then use parsing methods that can accept null values.

The first thing you probably need to do is use nullable types for your int/bool fields, so that you can support null values. Next, you'll need to create your own methods for parsing your ints/bools. If your input is null, return null, if not, use int.TryParse, bool.TryParse (or as for each if your input is the proper type, just cast to object).

Then by using those methods, instead of Convert, you won't be throwing exceptions in the first place (which you shouldn't be doing here even if it could work, because exceptions are for exceptional cases, not expected control flow).

Servy
  • 202,030
  • 26
  • 332
  • 449
  • In the case of a data-reader, it isn't really a case of *parsing* - it is checking whether it is null before casting – Marc Gravell Sep 03 '13 at 14:46
  • @MarcGravell It's unclear which it is, which is why I dislike the use of `Convert` in the first place. We don't know if he's parsing or casting. For all we know those int fields are VARCHAR fields in the database, or the booleans are coming back as 0/1 rather than as booleans. I do mention that if he doesn't actually require parsing, he should just be casting them with a null check, if that is indeed all that is needed. – Servy Sep 03 '13 at 14:50
7

If the exception is expected then it is not exceptional. Never never never catch a null reference exception. A null reference exception is a bug. Instead, write code to avoid the bug.

You can easily write helper methods that test for null, or use methods like Int32.TryParse that can handle malformed strings.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 2
    data-readers, however, involve a different kind of null - indeed, they aren't even `null` - which [IMO still causes more problems than it solves](http://stackoverflow.com/questions/4488727/what-is-the-point-of-dbnull/9632050#9632050) - the language doesn't have any nice ways of processing `DBNull` – Marc Gravell Sep 03 '13 at 15:09
3

Check for IsDBNull

SqlDataReader.IsDBNull Method

And Reader has methods for each SQL datatype
For example

SqlDataReader.GetSqlBoolean

If the data is in SQL as string (char,nchar) then first check for null and then TryParse
For example

DateTime.TryParse

And ordinal position is faster
This is a sample for a nullable Int16

Int16? ID; 
ID = rdr.IsDBNull(4) ? (Int16?)null : rdr.GetInt16(4);

If you want a default

Int16 ID; 
ID = rdr.IsDBNull(4) ? 0 : rdr.GetInt16(4);
paparazzo
  • 44,497
  • 23
  • 105
  • 176
1

You'd need a try/catch around every single variable assignment, and you'd need to initialize all your Column instance values before you tried. This would be relatively slow.

As for reflection based on the line number: I wouldn't rely on the line number because one simple, innocent change to the code will throw it off completely.

I'd check for nulls specifically. If you expect them you can't hardly call them "exceptions". The method that does that is reader.IsDBNull. It takes the column index (not the column name) so you'll need to resolve the index using reader.GetOrdinal:

if (reader.IsDBNull(reader.GetOrdinal("Data_Precision"))) {
  // It's null
} else {
  // It's not null
}
Ed Gibbs
  • 25,924
  • 4
  • 46
  • 69