views:

1421

answers:

5

Say I have this class:

class myclass
{
    public int Field1{ get; set; }
    public int? Field2 { get; set; } //Note Field2 is nullable
 }

I'm trying to populate a generic list with data coming from a database. As GetSqlInt32 implements INullable I would have thought that the below code would work. It doesn't. It generates an error if Field2 is null.

List<myclass> mylist=new List<myclass>();

int Field1_Ordinal = rdr.GetOrdinal("Field1");
int Field2_Ordinal = rdr.GetOrdinal("Field2");

SqlDataReader rdr = cmd.ExecuteReader(); //Execute a stored procedure to retrieve data from the database

while (rdr.Read())
 {
   mylist.Add(new myclass
   {
      Field1 = rdr.GetSqlInt32(Field1_Ordinal).Value,
      Field2 = rdr.GetSqlInt32(Field2_Ordinal).Value  //Error if field2 is null
   });
 }

Any ideas why it doesn't work?

A: 

DbNull.Value != null

So you need either a ? : expression or a if block to convert database nulls to c# nulls and vica versa.

MatthewMartin
+1  A: 

I think it is beacuse the value returned is DBNull.Value, and not null.

Instead you can use the IsDbNull() method to check if the field is null before reading it.

Rune Grimstad
The value returned is a SqlInt32, not a DBNull.Value.
Jon Skeet
+1  A: 

You have to use a special method on the reader to detect when the value is null

mylist.Add(new myclass   
{      
     Field1 = rdr.IsDbNull(Field1_Ordinal)? 0: 
               rdr.GetSqlInt32(Field1_Ordinal).Value,      
     Field2 = rdr.IsDbNull(Field2_Ordinal)? 0:  // whatever default value you wish...
               rdr.GetSqlInt32(Field2_Ordinal).Value  // No error now
});
Charles Bretana
in my case: Field2 = rdr.IsDbNull(Field2_Ordinal)? (int?) null: rdr.GetSqlInt32(Field2_Ordinal).ValueThanks for your answer.
Anthony
+2  A: 

It seems to me that you need a conversion like this (using an extension method for convenience):

public static int? ToNullableInt32(this SqlInt32 value)
{
    return value.IsNull ? (int?) null : value.Value;
}

Then:

Field2 = rdr.GetSqlInt32(Field2_Ordinal).ToNullableInt32()

(Comment on other answers: there's no need to bring DbNull into this, as SqlInt32 can already represent null values. You just need to detect that before using Value.)

Jon Skeet
Thanks for this but why not something like this without using an extension method:Field2 = (rdr.IsDBNull(Field2_Ordinal) ? (int?)null : rdr.GetSqlInt32(Field2_Ordinal).Value)
Anthony
@Anthony: Simplicity, basically. Which version do you think is easier to read? :) (Yes, it means having the additional extension method, but you only need that *once*, however many nullable int fields you have.)
Jon Skeet
You could also create an extension method on DataReader called GetNullableInt32 or something like that, of course.
Jon Skeet
@Jon: an extension method on DataReader is probably the best way. Once created, I don't have to worry about it. Thanks.
Anthony
+1  A: 

Here's a pain reduction variation on the theme. If someone knows how to merge Val and Ref into one template function fell free to post. You will have to state the type explicitly (C# compiled can't be bothered :-) but this:

var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);

is still maked my fingers happy :-)

public static T Def<T>(this SqlDataReader r, int ord)
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return default(T);
    return ((INullable)t).IsNull ? default(T) : (T)t;
}

public static T? Val<T>(this SqlDataReader r, int ord) where T:struct
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? (T?)null : (T)t;
}

public static T Ref<T>(this SqlDataReader r, int ord) where T : class
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? null : (T)t;
}
ZXX
Thank you for posting this.
AspNyc