Как использовать datareader со значениями null

скажем, у меня есть этот класс:

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

Я пытаюсь заполнить общий список с данными из базы данных. Поскольку GetSqlInt32 реализует INullable, я бы подумал, что приведенный ниже код будет работать. Это не так. Он генерирует ошибку, если Field2 равно 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
   });
 }

есть идеи, почему это не работает?

7 ответов


мне кажется, что вам нужно такое преобразование (используя метод расширения для удобства):

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

затем:

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

(комментарий к другим ответам: нет необходимости приносить DbNull В, а SqlInt32 уже может представлять значения null. Вам просто нужно обнаружить это перед использованием Value.)


проверить это решение, которое не было написано мной:

employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);

первоначально это было предложено здесь:

SQL Data Reader-обработка значений нулевого столбца


вот вариация уменьшения боли на эту тему. Если кто-то знает, как объединить Val и Ref в один шаблон, функция может свободно публиковать. Вам нужно будет явно указать тип (C# compiled не может беспокоить : -), но это:

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

по-прежнему делает мои пальцы счастливыми : -)

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;
}

Я думаю это beacuse возвращаемое значение DBNull.Value, а не null.

вместо этого вы можете использовать метод IsDbNull (), чтобы проверить, является ли поле null перед его чтением.


вы должны использовать специальный метод для чтения, чтобы определить, когда значение равно 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
});

Я пытаюсь экспортировать базу данных Access с 39 полями-многие с нулевыми значениями. Я не мог заставить метод расширения работать, поэтому я написал следующую функцию:

private string ChkDbStr(object inObj)
{
  if (inObj == null)
  { return ""; }
  else
  { return inObj.ToString(); }
}

когда я читаю каждое поле, которое может содержать NULL, я кодирую поле как: ChkDbStr (DbReader.GetValue (1))


значение dbnull.Ценность != null

Так вам нужен либо a ? : выражение или блок if для преобразования нулей базы данных в нули c# и наоборот.