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

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

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

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


person Anthony    schedule 01.09.2009    source источник


Ответы (7)


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

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

Затем:

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

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

person Jon Skeet    schedule 01.09.2009
comment
Спасибо за это, но почему бы не сделать что-то подобное без использования метода расширения: Field2 = (rdr.IsDBNull(Field2_Ordinal) ? (int?)null : rdr.GetSqlInt32(Field2_Ordinal).Value) - person Anthony; 01.09.2009
comment
@Энтони: Простота, в основном. Как вы думаете, какую версию легче читать? :) (Да, это означает наличие дополнительного метода расширения, но он вам нужен только один раз, сколько бы полей int, допускающих значение NULL, у вас не было.) - person Jon Skeet; 01.09.2009
comment
Вы также можете создать метод расширения для DataReader с именем GetNullableInt32 или что-то в этом роде, конечно. - person Jon Skeet; 01.09.2009
comment
@Jon: метод расширения для DataReader, вероятно, лучший способ. После создания мне не нужно об этом беспокоиться. Спасибо. - person Anthony; 01.09.2009
comment
Должен ли reader.GetFieldValue‹int?› обрабатывать нулевые значения? - person Farinha; 26.09.2013
comment
@Farinha: Честно говоря, я не знаю, так ли это. В документации для DbReader.GetFieldValue не упоминается об этом... - person Jon Skeet; 26.09.2013
comment
Вы правы, в документации для DbReader.GetFieldValue даже указано, какие типы он поддерживает, а int? не является одним из них. Пошел с созданием метода расширения, как было предложено. - person Farinha; 26.09.2013

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

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

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

Считыватель данных SQL — обработка нулевых значений столбца

person Ignacio Soler Garcia    schedule 26.08.2011

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

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;
}
person ZXX    schedule 22.07.2010

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

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

person Rune Grimstad    schedule 01.09.2009
comment
Возвращаемое значение — это SqlInt32, а не DBNull.Value. - person Jon Skeet; 01.09.2009

Вы должны использовать специальный метод для чтения, чтобы определить, когда значение равно нулю.

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
});
person Charles Bretana    schedule 01.09.2009
comment
в моем случае: Field2 = rdr.IsDbNull(Field2_Ordinal)? (int?) null: rdr.GetSqlInt32(Field2_Ordinal).Value Спасибо за ответ. - person Anthony; 01.09.2009

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

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

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

person TcTom    schedule 06.12.2010

DbNull.Value != ноль

Итак, вам нужно либо ? : выражение или блок if для преобразования нулевых значений базы данных в нулевые значения C# и наоборот.

person MatthewMartin    schedule 01.09.2009