Как лучше всего справиться с DBNull's

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

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

Как лучше всего справиться с DBNull's в такой ситуации?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
44
1
56 909
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

Ответ принят как подходящий

Типы, допускающие значение NULL, хороши, но только для типов, которые изначально не допускают значения NULL.

Чтобы сделать тип "допускающим значение NULL", добавьте к типу вопросительный знак, например:

int? value = 5;

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

  1. Если тип допускает значение NULL, ключевое слово «as» возвращает null, если значение равно DBNull.
  2. Это немного быстрее, чем кастинг, хотя только в определенных случаях. Само по себе это никогда не является достаточной причиной для использования as, но в сочетании с вышеуказанной причиной это полезно.

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

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

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

Это очень старый пост, однако стоит отметить, что ключевое слово as может использоваться для приведения только для ссылочных типов и типов, допускающих значение NULL, но не для типов значений.

Div Tiwari 02.06.2020 15:56

Вы также можете протестировать с помощью Convert.IsDBNull (MSDN).

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

Это будет примерно так:

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

+1: этот ответ должен получить больше голосов и, вероятно, также принят - определенно row.Field("имя_столбца") - наиболее эффективное, удобочитаемое и чистое решение для работы с нулевым значением (DBNull.Value). Также есть row.SetField("имя_столбца", значение) для записи значения в строку.

Pavel Hodek 20.12.2012 03:20

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

Если вы не используете типы, допускающие значение NULL, лучше всего проверить, является ли значение столбца 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"]);
}

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

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

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

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

Строка if (row ["fooColumn"] == DBNull.Value) работает, но неверна. Он не определен. DBNull.Value должен быть реализован как шаблон Singleton. Лучшей строкой было бы: if (row ["fooColumn"] - DBNull)

doekman 12.06.2009 12:30

@doekman - Фактически, DBNull является одноэлементный класс. Процитируем MSDN: «DBNull - это одноэлементный класс, что означает, что может существовать только этот экземпляр [DBNull.Value] этого класса». msdn.microsoft.com/en-us/library/system.dbnull.value.aspx

Greg 22.12.2009 19:39

Если вы хотите рассматривать dbnull как пустую строку row ["fooColumn"]. ToString () сделает это.

samir105 23.12.2012 12:27

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

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

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

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

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

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

Итак, предполагая, что столбец Int32, допускающий значение NULL, с именем «MyCol», мы хотим вернуть -99, если столбец имеет значение NULL, но вернуть целочисленное значение, если столбец не равен NULL:

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

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

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"]);

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

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

Однако иногда важно знать разницу между Null и пустой строкой. Например, установка значения для пустой строки, в отличие от значения, которое просто никогда не устанавливается.

Mykroft 24.10.2008 21:44

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

В резюме «ИЗБЕГАЙТЕ использования 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?

Сейчас мы используем ORM. В то время мы не были

Mykroft 11.02.2009 00:37
По какой причине вы не используете ORM? I use raw ADO .NET for performance. Try to merge 1 million records with EF or NH.
ta.speot.is 22.09.2013 14:58

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

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

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

Это не работает, если значение не является строкой. Я искал общий ответ.

Mykroft 24.03.2009 20:04

Я согласен, да, это хорошо, если строки сохраняют простоту, но другие типы не будут работать, например. попробуйте сделать bool.Parse (row ["fooColumn"]. ToString ()).

PeteT 15.07.2009 18:49

Вы также должны посмотреть на методы расширения. Здесь - несколько примеров для работы с этим сценарием.

Рекомендуемый читать

Стоит отметить, что 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);

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

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

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

Другие вопросы по теме