41

Is there a better/cleaner way to do this?

int stockvalue = 0;
if (!Convert.IsDBNull(reader["StockValue"]))
    stockvalue = (int)reader["StockValue"];
John Saunders
  • 160,644
  • 26
  • 247
  • 397
Andreas
  • 451
  • 1
  • 4
  • 3
  • You should also consider extension methods. Here are couple of examples provided for other possible ways as well: http://shahanayyub.wordpress.com/2012/10/04/best-practice-to-check-for-dbnull-using-net/ – NeverHopeless Oct 13 '12 at 15:07

13 Answers13

65

The shortest (IMHO) is:

int stockvalue = (reader["StockValue"] as int?) ?? 0;

Explanation:

  • If reader["StockValue"] is of type int, the value will be returned, and the "??" operator will return the result
  • If reader["StockValue"] is NOT of type int (e.g. DBNull), null will be returned, and the "??" operator will return the value 0 (zero).
Philippe Leybaert
  • 168,566
  • 31
  • 210
  • 223
  • 3
    @Philippe: what happens if the type of the column is changed to Double or something? – John Saunders Mar 12 '10 at 14:37
  • 1
    +1 - This is the way I'd write the code, although if a null value is valid there is no need for the default; `int? stockvalue = reader["StockValue"] as int?` – stevehipwell Mar 12 '10 at 14:38
  • 2
    @John: it would return 0. But no matter what solution you pick, if a column changes data type, you're screwed, unless you throw in a call to Convert.ToInt32() or something. – Philippe Leybaert Mar 12 '10 at 14:44
  • 4
    But with an explicit unboxing cast `int x = (int)obj`, your code will break when the column's type changes, allowing you to correct it to `double x = (double)obj`; @JohnSaunders is correct to raise a red flag, since the `as int?` approach would silently swallow this change; you would only be able to catch it if someone noticed a zero value where there shouldn't be one. – phoog Feb 24 '12 at 19:46
  • phoog is totally right. If a column is not an int as you expect, the result was always 0. Imagine others trying to read this code to find out why the result is always 0? The solution by Digicoder below is easier to read and more flexible. Just use the basic if shorthand. – Frug Sep 11 '12 at 16:55
31

The way I handle this is

int? stockvalue = reader["StockValue"] as int?;

Very simple, clean and one line. If for some reason I absolutely can't have a null value (which I find poor reasoning for usually since I'd rather know if a value has meaning or if it was unitialized for a primitive type) I would do:

int stockvalue = (reader["StockValue"] as int?).GetValueOrDefault(-1);
Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
  • Ahh, I got an exception using `(int?) reader["ColumnName"]` and began searching for the proper way to do it. This should be accepted as answer. – Anlo Mar 28 '13 at 16:18
  • Excellent. This also preserves the DBNull state in case one would be interested in knowing that. Some other solutions here discards this. – Jonas Feb 18 '14 at 10:07
13

I wrote an extension method several days ago. By using it you could just do:

int? stockvalue = reader.GetValue<int?>("StockValue");

Here's the extension method (modify to fit your needs):

public static class ReaderHelper
{
    public static bool IsNullableType(Type valueType)
    {
        return (valueType.IsGenericType &&
            valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
    }

    public static T GetValue<T>(this IDataReader reader, string columnName)
    {
        object value = reader[columnName];
        Type valueType = typeof(T);
        if (value != DBNull.Value)
        {
            if (!IsNullableType(valueType))
            {
                return (T)Convert.ChangeType(value, valueType);
            }
            else
            {
                NullableConverter nc = new NullableConverter(valueType);
                return (T)Convert.ChangeType(value, nc.UnderlyingType);
            }
        }
        return default(T);
    }
}
Vivek
  • 16,360
  • 5
  • 30
  • 37
  • 2
    I absolutely love C#, and have code similar to this in a place or 2, but it's absolutely retarded we ever would need to write code like this. I hope with continued advancements in the DLR that there will be a day that we never need to write code like this to do type wrangling. – Chris Marisic May 26 '11 at 17:43
10
int? stockvalue = (int?)(!Convert.IsDBNull(result) ? result : null);

One possible solution so that you ensure that the DBNull carries across to your code. For our group, as a best practice, we try and not allow NULL columns in the database unless its really needed. There is more overhead in coding to handle it, and sometimes just rethinking the problem makes it so its not required.

Digicoder
  • 1,835
  • 1
  • 12
  • 21
  • I think this solution is the most clear and flexible. I went with something nearly identical but not allowing nulls: Int32 aNum = Convert.IsDBNull(row["stockvalue"]) ? 0 : Convert.ToInt32(row["stockvalue"]) – Frug Sep 11 '12 at 16:53
7

Yes you can use int? This way you can have a default value of null instead of 0. Since the result of stockvalue could potentially be 0 there isn't confusion as to whether the database was 0 or null. For instance like this (pre nullable) we had a default initialization of -1 to represent no value was assigned. Personally, I thought this was a little dangerous because if you forget to set it to -1, there is a data corruption issue that can be really difficult to track down.

http://msdn.microsoft.com/en-us/library/2cf62fcy(VS.80).aspx

int? stockvalue = null;

if (!Convert.IsDBNull(reader["StockValue"]))
    stockvalue = (int)reader["StockValue"];

//Then you can check 

if(stockValue.HasValue)
{
  // do something here.
}
kemiller2002
  • 113,795
  • 27
  • 197
  • 251
7

While it's convenient to reference reader["StockValue"], it's not very efficient. It's also not strongly-typed, as it returns type object.

Instead, within your code, do something like this:

int stockValueOrdinal = reader.GetOrdinal("StockValue");
int? stockValue = reader.IsDbNull(stockValueOrdinal) ?
    null : 
    reader.GetInt32(stockValueOrdinal);

Of course, it's best to get all of the ordinals at one time, then use them throughout the code.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
  • @John: in your comment on my answer you asked me "what if the column changed to double?". The code you provided here will throw an exception if the column holds a value which is not of type int. (also, minor detail: there's not .GetInt() method on a data reader. It should be .GetInt32()) – Philippe Leybaert Mar 12 '10 at 14:53
  • 3
    @Philippe: throwing an exception is _precisely_ what the code should do if the column type changes and the code does not. Returning zero when something serious is wrong is a very bad idea. – John Saunders Mar 12 '10 at 16:21
3
int stockvalue = reader["StockValue"] != DbNull.Value ? Convert.ToInt32(reader["StockValue"]) : 0;
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
amin10043
  • 234
  • 3
  • 7
1

Here's one way.

int stockvalue = Convert.IsDbNull(reader["StockValue"]) ? 0 : (int)reader["StockValue"];

You could also use TryParse

int stockvalue = 0
Int32.TryParse(reader["StockValue"].ToString(), out stockvalue);

Let us know which way works for you

sidney.andrews
  • 5,146
  • 3
  • 23
  • 29
1

You could do this conversion directly in your DB-query, thus avoiding the special case alltogether.

But I wouldn't call that 'cleaner', unless you can consistently use that form in your code, since you would lose information by returning '0' instead of NULL from the DB.

lexu
  • 8,766
  • 5
  • 45
  • 63
  • The argument about losing information does make sense. In an database application, the value of "0" is different than NULL. One denotes having no value at all, and one is a value that just happens to be zero. – sidney.andrews Mar 12 '10 at 14:02
0

use the Nullable<int> type...int? for short

Rich
  • 36,270
  • 31
  • 115
  • 154
0

Not really. You could encapsulate it in a method:

public int getDBIntValue(object value, int defaultValue) {
  if (!Convert.IsDBNull(value)) {
    return (int)value;
  }
  else {
    return defaultValue;
  }

And call it like this:

stockVaue = getDBIntVaue(reader["StockValue"], 0);

Or you could use coalesce in your query to force the returned value to be non-null.

Edit - corrected dumb code errors based on comments received.

Ray
  • 21,485
  • 5
  • 48
  • 64
  • 1
    (I didn't downvote you, but) For one, you passed in reader["StockValue"] in the "value" parameter, but then ignored the "value" parameter inside the function and used "reader["StockValue"]" instead, so it won't compile. – Adam V Mar 12 '10 at 14:10
  • @Ray: Your `getDBIntValue` will always attempt to extract an `int` from `reader["StockValue"]`, regardless of what's passed in. You also have the keyword `default` as one of your parameters, which won't compile (you'd have to make it `@default`, or -- better -- just change the name). – Dan Tao Mar 12 '10 at 14:10
  • @John - I corrected a copy-and-paste error which I made. However, the "if you don't know I'm not going to tell you" thing doesn't help me (or anyone else reading these comments) to learn. – Ray Mar 12 '10 at 14:11
  • @Dan: presumably he realizes it'll always get an int, hence the name getDB*Int*Vaue (btw, Ray, please add the missing 'l'. Misspellings bother me). – Adam V Mar 12 '10 at 14:15
  • This will teach me to just type and post without checking my code- sorry for the dumb errors - I think it is fixed now. – Ray Mar 12 '10 at 14:15
  • @adam - the spelling is fixed - my 'L' key is sticky today. And yes, you have to know you are getting an int. If one were to use these types of utility methods, you would have getDBString, getDBByte, etc. – Ray Mar 12 '10 at 14:19
  • @Adam: Originally (before he fixed the code) the method called `Convert.IsDBNull(reader["StockValue"])` regardless of the parameter passed. So it would *always* look at `reader["StockValue"]`, not any other column in any other table. That was what I was pointing out--not that it returns an `int` (which you're correct in pointing out should be obvious). – Dan Tao Mar 12 '10 at 15:15
0

I have two following extension methods in my project:

    public static T GetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor)
        where T : class 
    {
        T value;
        if (dataReader.TryGetValueSafe(columnName, valueExtractor, out value))
        {
            return value;
        }

        return null;
    }

    public static bool TryGetValueSafe<T>(this IDataReader dataReader, string columnName, Func<IDataReader, int, T> valueExtractor, out T value)
    {
        int ordinal = dataReader.GetOrdinal(columnName);

        if (!dataReader.IsDBNull(ordinal))
        {
            // Get value.
            value = valueExtractor.Invoke(dataReader, ordinal);

            return true;
        }

        value = default(T);
        return false;
    }

The usage can be like this:

string companyName = dataReader.GetValueSafe("CompanyName", (reader, ordinal) => reader.GetString(ordinal));
Andrew Bezzub
  • 15,744
  • 7
  • 51
  • 73
  • 1
    Interesting but very wordy, I created a class similar to this when I worked with DataReaders extensively but made it much more concise that it operated as `reader.Get("ReaderField")` and worked using the `ChangeType` method – Chris Marisic Mar 12 '10 at 18:02
  • Good idea +1, but I think my implementation would be a bit faster. – Andrew Bezzub Mar 12 '10 at 20:07
0
int? stockValue = reader["StockValue"] == null || reader["StockValue"] == DBNull.Value ? null : (int?)reader["StockValue"];
Will Marcouiller
  • 23,773
  • 22
  • 96
  • 162