У меня часто возникают проблемы с DataRows, возвращенным из SqlDataAdapters. Когда я пытаюсь заполнить объект, используя такой код:
DataRow row = ds.Tables[0].Rows[0];
string value = (string)row;
Как лучше всего справиться с DBNull's в такой ситуации?





Типы, допускающие значение NULL, хороши, но только для типов, которые изначально не допускают значения NULL.
Чтобы сделать тип "допускающим значение NULL", добавьте к типу вопросительный знак, например:
int? value = 5;
Я также рекомендовал бы использовать ключевое слово «as» вместо приведения. Вы можете использовать ключевое слово as только для типов, допускающих значение NULL, поэтому убедитесь, что вы приводите объекты, которые уже допускают значение NULL (например, строки), или используете типы, допускающие значение NULL, как упоминалось выше. Причина этого в том, что
as» возвращает null, если значение равно DBNull.as, но в сочетании с вышеуказанной причиной это полезно.Я бы порекомендовал сделать что-то подобное
DataRow row = ds.Tables[0].Rows[0];
string value = row as string;
В приведенном выше случае, если row возвращается как DBNull, тогда value станет null вместо того, чтобы генерировать исключение. Имейте в виду, что если ваш запрос к БД изменяет возвращаемые столбцы / типы, использование as приведет к тому, что ваш код не сработает и делает значения простыми null вместо того, чтобы генерировать соответствующее исключение при возвращении неверных данных, поэтому рекомендуется иметь тесты для проверки ваших запросов другими способами, чтобы Обеспечьте целостность данных по мере развития вашей кодовой базы.
Это очень старый пост, однако стоит отметить, что ключевое слово as может использоваться для приведения только для ссылочных типов и типов, допускающих значение NULL, но не для типов значений.
Вы также можете протестировать с помощью 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
Обычно я пишу свой собственный класс 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, пример кода поможет.
Строка if (row ["fooColumn"] == DBNull.Value) работает, но неверна. Он не определен. DBNull.Value должен быть реализован как шаблон Singleton. Лучшей строкой было бы: if (row ["fooColumn"] - DBNull)
@doekman - Фактически, DBNull является одноэлементный класс. Процитируем MSDN: «DBNull - это одноэлементный класс, что означает, что может существовать только этот экземпляр [DBNull.Value] этого класса». msdn.microsoft.com/en-us/library/system.dbnull.value.aspx
Если вы хотите рассматривать dbnull как пустую строку row ["fooColumn"]. ToString () сделает это.
По какой-то причине у меня возникли проблемы с проверкой 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 и пустой строкой. Например, установка значения для пустой строки, в отличие от значения, которое просто никогда не устанавливается.
Брэд Абрамс опубликовал что-то похожее всего пару дней назад 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. В то время мы не были
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
Я полагаю, это лучшая практика, которую вы ищете
Это не работает, если значение не является строкой. Я искал общий ответ.
Я согласен, да, это хорошо, если строки сохраняют простоту, но другие типы не будут работать, например. попробуйте сделать bool.Parse (row ["fooColumn"]. ToString ()).
Стоит отметить, что 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 в строку, и '??' возвращается член справа от выражения, если первое значение равно нулю.
закрыть: самый эффективный-способ-проверить-для-dbnull-and-then-assign-to-a-переменной