Каков наилучший способ решения проблемы с DBNull?

У меня часто возникают проблемы с DataRows, возвращаемыми из SqlDataAdapters. Когда я пытаюсь заполнить объект, используя следующий код:

DataRow row = ds.Tables[0].Rows[0];
string value = (string)row;

Каков наилучший способ справиться с DBNull's в этом типе ситуации.

Ответ 1

Нулевые типы хороши, но только для типов, которые не могут быть нулевыми для начала.

Чтобы сделать тип "nullable", добавьте знак вопроса к типу, например:

int? value = 5;

Я бы также рекомендовал использовать ключевое слово < as, а не кастинг. Вы можете использовать только ключевое слово "как" для типов с нулевым значением, поэтому убедитесь, что вы выбрали вещи, которые уже обнуляются (например, строки), или вы используете типы с нулевым значением, как указано выше. Причиной этого является

Я бы рекомендовал сделать что-то вроде этого

DataRow row = ds.Tables[0].Rows[0];
string value = row as string;

В приведенном выше случае, если row возвращается как DBNull, то value станет null вместо того, чтобы выбрасывать исключение. Имейте в виду, что если ваш запрос БД изменяет возвращаемые столбцы/типы, с помощью as приведет к тому, что ваш код будет терпеть неудачу и сделает значения простыми null вместо того, чтобы выбрасывать соответствующее исключение, когда неправильные данные поэтому рекомендуется, чтобы у вас были тесты для проверки ваших запросов другими способами, чтобы обеспечить целостность данных по мере развития вашей кодовой базы.

Ответ 2

Если вы не используете типы с нулевым значением, лучше всего проверить, является ли значение столбца DBNull. Если это DBNull, то установите ссылку на то, что вы используете для null/empty для соответствующего типа данных.

DataRow row = ds.Tables[0].Rows[0];
string value;

if (row["fooColumn"] == DBNull.Value)
{
   value = string.Empty;
}
else 
{
   value = Convert.ToString(row["fooColumn"]);
}

Как сказал Ману, вы можете создать класс преобразования с перегруженным методом конвертации для каждого типа, чтобы вам не нужно перекосить код с блоками if/else.

Однако я хочу подчеркнуть, что типы с нулевыми значениями - лучший путь для перехода, если вы можете их использовать. Причиной является то, что с непустыми типами вам придётся прибегать к "магическим числам" для представления нулевого значения. Например, если вы сопоставляете столбец с переменной int, как вы собираетесь представлять DBNull? Часто вы не можете использовать 0, потому что 0 имеет действительное значение в большинстве программ. Часто я вижу, что люди отображают DBNull в int.MinValue, но это также может быть проблематично. Мой лучший совет:

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

Были решены нулевые типы для решения этой проблемы. При этом, если вы используете более старую версию фреймворка или работаете для тех, кто не заражает типы с нулевым значением, пример кода будет делать трюк.

Ответ 3

Я всегда находил это ясным, кратким и беспроблемным, используя версию проверки If/Else, только с тернарным оператором. Сохраняет все в одной строке, включая присвоение значения по умолчанию, если столбец имеет значение null.

Итак, предположим, что столбец Int32 с нулевым значением с именем "MyCol", где мы хотим вернуть -99, если столбец имеет значение null, но возвращает целочисленное значение, если столбец не равен null:

return row["MyCol"] == DBNull.Value ? -99 : Convert.ToInt32(Row["MyCol"]);

Это тот же метод, что и победитель If/Else выше. Но я обнаружил, что если вы читаете несколько столбцов из datareader, это реальный бонус, имеющий все строки, считывающие столбцы один под другим, выстроенный вверх, так как легче обнаружить ошибки:

Object.ID = DataReader["ID"] == DBNull.Value ? -99 : Convert.ToInt32(DataReader["ID"]);
Object.Name = DataReader["Name"] == DBNull.Value ? "None" : Convert.ToString(DataReader["Name"]);
Object.Price = DataReader["Price"] == DBNull.Value ? 0.0 : Convert.ToFloat(DataReader["Price"]);

Ответ 4

Добавьте ссылку на System.Data.DataSetExtensions, которая добавляет поддержку Linq для запроса таблиц данных.

Это будет что-то вроде:

string value = (
    from row in ds.Tables[0].Rows
    select row.Field<string>(0) ).FirstOrDefault();

Ответ 5

Если у вас есть контроль над запросом, возвращающим результаты, вы можете использовать ISNULL() для возврата ненулевых значений, например:

SELECT 
  ISNULL(name,'') AS name
  ,ISNULL(age, 0) AS age
FROM 
  names

Если ваша ситуация может переносить эти магические значения, чтобы заменить NULL, этот подход может устранить проблему через ваше приложение без загромождения кода.

Ответ 6

DBNull реализует .ToString(), как и все остальное. Не нужно ничего делать. Вместо жесткого литья вызовите метод objectToString().

DataRow row = ds.Tables[0].Rows[0];
string value;

if (row["fooColumn"] == DBNull.Value)
{
   value = string.Empty;
}
else 
{
   value = Convert.ToString(row["fooColumn"]);
}

это будет:

DataRow row = ds.Tables[0].Rows[0];
string value = row.ToString()

DBNull.ToString() возвращает string.Empty

Я бы предположил, что это лучшая практика, которую вы ищете

Ответ 7

Следует отметить, что DBNull.Value.ToString() равно String.Empty

Вы можете использовать это в своих интересах:

DataRow row = ds.Tables[0].Rows[0];
string value = row["name"].ToString();

Однако это работает только для строк, для всего остального я бы использовал метод linq или метод расширения. Для себя я написал небольшой метод расширения, который проверяет DBNull и даже делает кастинг через Convert.ChangeType(...)

int value = row.GetValueOrDefault<int>("count");
int value = row.GetValueOrDefault<int>("count", 15);

Ответ 8

Обычно я пишу свой собственный класс ConvertDBNull, который обертывает встроенный класс Convert. Если значение DBNull, оно возвращает null, если это ссылочный тип или значение по умолчанию, если оно имеет тип значения. Пример: - ConvertDBNull.ToInt64(object obj) возвращает Convert.ToInt64(obj), если obj не DBNull, в этом случае он вернет 0.

Ответ 9

Часто при работе с DataTables вам приходится иметь дело с этими случаями, когда поле строки может быть либо нулевым, либо DBNull, как правило, я занимаюсь следующим:

string myValue = (myDataTable.Rows[i]["MyDbNullableField"] as string) ?? string.Empty;

Оператор 'as' возвращает значение null для недопустимых значений, например DBNull для строки, и '??' возвращается член справа от выражения, если первый имеет значение null.

Ответ 11

По какой-то причине у меня были проблемы с выполнением проверки с помощью DBNull.Value, поэтому я немного изменил и использовал свойство внутри объекта DataRow:

if (row.IsNull["fooColumn"])
{
   value = string.Empty();
}
{
else
{
   value = row["fooColumn"].ToString;
}

Ответ 12

Брэд Абрамс опубликовал что-то, связанное всего пару дней назад http://blogs.msdn.com/brada/archive/2009/02/09/framework-design-guidelines-system-dbnull.aspx

В сводке "AVOID using System.DBNull. Предпочитает вместо Nullable."

И вот мои два цента (из непроверенного кода:))

// Or if (row["fooColumn"] == DBNull.Value)
if (row.IsNull["fooColumn"])
{
   // use a null for strings and a Nullable for value types 
   // if it is a value type and null is invalid throw a 
   // InvalidOperationException here with some descriptive text. 
   // or dont check for null at all and let the cast exception below bubble  
   value = null;
}
else
{
   // do a direct cast here. dont use "as", "convert", "parse" or "tostring"
   // as all of these will swallow the case where is the incorect type.
   // (Unless it is a string in the DB and really do want to convert it)
   value = (string)row["fooColumn"];
}

И один вопрос... Любая причина, по которой вы не используете ORM?

Ответ 14

Если вы заинтересованы в получении DBNull при ожидании строк, одним из вариантов является преобразование всех значений DBNull в DataTable в пустую строку.

Это довольно просто сделать, но это добавит некоторые накладные расходы, особенно если вы имеете дело с большими DataTables. Проверьте ссылку, которая показывает, как это сделать, если вы заинтересованы

Ответ 15

попробуйте это

  if (ds.Tables[0].Rows[0].ItemArray[0] == System.DBNull.Value)
  {
    //DBNull
  }

Полный исходный код: Значение DBNull

Rj