Какой смысл в DBNull? - PullRequest
       16

Какой смысл в DBNull?

68 голосов
/ 20 декабря 2010

В .NET есть ссылка null, которая используется везде для обозначения того, что ссылка на объект пуста, а затем есть DBNull, которая используется драйверами базы данных (и некоторыми другими) для обозначения... почти то же самоеЕстественно, это создает много путаницы, и необходимо преобразовать процедуры преобразования и т. Д.

Так почему же авторы .NET решили сделать это?Для меня это не имеет смысла.Их документация также не имеет смысла:

Класс DBNull представляет несуществующее значение.Например, в базе данных столбец в строке таблицы может не содержать никаких данных.То есть считается, что столбец вообще не существует, а просто не имеет значения.Объект DBNull представляет несуществующий столбец.Кроме того, COM-взаимодействие использует класс DBNull, чтобы различать вариант VT_NULL, который указывает на несуществующее значение, и вариант VT_EMPTY, который указывает неопределенное значение.

Что это за хрень про "столбец не существует"?Столбец существует, у него просто нет значения для конкретной строки.Если бы он не существовал, я бы получил исключение при попытке доступа к определенной ячейке, а не DBNull!Я могу понять необходимость различать VT_NULL и VT_EMPTY, но тогда почему бы вместо этого не создать класс COMEmpty?Это было бы намного лучше в целом .NET Framework.

Я что-то упустил?Может кто-нибудь пролить свет на то, почему был изобретен DBNull и какие проблемы он помогает решать?

Ответы [ 5 ]

165 голосов
/ 09 марта 2012

Я собираюсь не согласиться с трендом здесь. Я пойду на запись:

Я не согласен с тем, что DBNull служит какой-либо полезной цели; это добавляет ненужную путаницу, хотя и не вносит никакой пользы.

Часто выдвигается аргумент, что null - недопустимая ссылка, а DBNull - шаблон нулевого объекта; ни то, ни другое . Например:

int? x = null;

это не "недействительная ссылка"; это null значение. Действительно, null означает, что вы хотите, чтобы это значило, и, честно говоря, у меня нет абсолютно никаких проблем с работой со значениями, которые могут быть null (действительно, даже в SQL нам нужно правильно работать с null - здесь ничего не меняется). Точно так же «шаблон нулевого объекта» имеет смысл, только если вы фактически рассматриваете его как объект в терминах ООП, но если у нас есть значение, которое может быть «нашим значением или DBNull», тогда оно должно быть object поэтому мы не можем ничего с этим сделать полезного.

Есть так много плохих вещей с DBNull:

  • заставляет работать с object, поскольку только object может содержать DBNull или другое значение
  • нет реальной разницы между "может быть значением" или DBNull "против" может быть значением или null "
  • аргумент, что это происходит из 1.1 (pre-nullable-types), не имеет смысла; мы могли бы прекрасно использовать null в 1.1
  • У большинства API есть "это ноль?" методы, например DBDataReader.IsDBNull или DataRow.IsNull - ни один из которых на самом деле не требует DBNull существования в терминах API
  • DBNull терпит неудачу с точки зрения объединения нулей; value ?? defaultValue не работает, если значение DBNull
  • DBNull.Value нельзя использовать в необязательных параметрах, поскольку он не является константой
  • семантика времени выполнения DBNull идентична семантике null; в частности, DBNull фактически равняется DBNull - так что не не выполняет работу по представлению семантики SQL
  • это часто вынуждает упаковывать значения типа значения, так как он чрезмерно использует object
  • если вам нужно проверить на DBNull, вы могли бы также проверить на null
  • это вызывает огромные проблемы для таких вещей, как параметры команды, с очень глупым поведением, что если параметр имеет значение null, он не отправляется ... ну, вот идея: если вы не хотите отправленный параметр - не добавлять его в коллекцию параметров
  • каждый ORM, который я могу придумать, прекрасно работает без любой необходимости или использования DBNull, за исключением случаев, когда возникает дополнительная неприятность при разговоре с кодом ADO.NET

Единственный даже удаленно убедительный аргумент, который я когда-либо видел, чтобы оправдать существование такого значения, находится в DataTable при передаче значений создать новую строку; null означает «использовать значение по умолчанию», DBNull явно равен нулю - честно говоря, этот API мог бы иметь конкретную обработку для этого случая - например, воображаемый DataRow.DefaultValue был бы намного лучше, чем введение DBNull.Value без какой-либо причины заражает обширные участки кода.

Точно так же сценарий ExecuteScalar ... в лучшем случае незначителен; если вы выполняете скалярный метод, вы ожидаете результата. В сценарии, где нет строк, возврат null не кажется слишком ужасным. Если вам абсолютно необходимо устранить неоднозначность между «без строк» ​​и «возвращен один ноль», есть API для чтения.

Этот корабль давно отплыл, и уже слишком поздно его чинить. Но! Пожалуйста, сделайте не и подумайте, что все согласны с тем, что это "очевидная" вещь. Многие разработчики не видят значение в этой странной складке на BCL.

Мне действительно интересно, все ли это проистекает из двух вещей:

  • необходимость использовать слово Nothing вместо чего-то, включающего "ноль" в VB
  • имея возможность использовать синтаксис if(value is DBNull), который "выглядит как SQL", а не как о-о-хитро if(value==null)

Резюме:

Наличие 3-х опций (null, DBNull или фактическое значение) полезно только в том случае, если есть подлинный пример, в котором вам необходимо устранить неоднозначность между 3 разными случаями.Я еще не видел случая, когда мне нужно было бы представлять два разных «нулевых» состояния, поэтому DBNull полностью избыточен, поскольку null уже существует и имеет гораздо лучшую поддержку языка и среды выполнения.

45 голосов
/ 20 декабря 2010

Дело в том, что в определенных ситуациях существует разница между значением базы данных, равным NULL, и значением .NET, равным NULL.

Например. Если вы используете ExecuteScalar (который возвращает первый столбец первой строки в наборе результатов) и вы получаете нулевое значение, это означает, что выполненный SQL не вернул никаких значений. Если вы возвращаете DBNull, это означает, что значение было возвращено SQL, и оно было NULL. Вы должны быть в состоянии отличить.

13 голосов
/ 20 декабря 2010

DbNull представляет ящик без содержимого; null указывает на отсутствие коробки.

1 голос
/ 20 декабря 2010

Вы используете DBNull для отсутствующих данных.В языке .NET значение NULL означает, что для объекта / переменной нет указателя.

DBNull: отсутствуют данные: http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx

Влияние отсутствующих данных на статистику:

http://en.wikipedia.org/wiki/Missing_values

0 голосов
/ 02 мая 2013

Существуют некоторые различия между нулем CLR и DBNull. Во-первых, нуль в реляционных базах данных имеет различную семантику «равно»: нуль не равен нулю. CLR null равно нулю.

Но я подозреваю, что основная причина заключается в том, как значения параметров по умолчанию работают на сервере SQL, и в реализации поставщика.

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

CREATE PROC [Echo] @s varchar(MAX) = 'hello'
AS
    SELECT @s [Echo]

Хорошо структурированный код DAL должен отделять создание команд от использования (чтобы разрешить многократное использование одной и той же команды, например, для эффективного вызова хранимой процедуры много раз). Напишите метод, который возвращает SqlCommand, представляющий вышеуказанную процедуру:

SqlCommand GetEchoProc()
{
    var cmd = new SqlCommand("Echo");
    cmd.Parameters.Add("@s", SqlDbType.VarChar);
    return cmd;
}

Если вы теперь вызываете команду без установки параметра @s или устанавливаете ее значение в (CLR) null, она будет использовать значение по умолчанию 'hello'. С другой стороны, если вы установите значение параметра в DBNull.Value, оно будет использовать его и выдает DbNull.Value.

Поскольку есть два разных результата, использующих значение CLR null или базу данных null в качестве значения параметра, вы не можете представить оба случая только с одним из них. Если бы CLR null был единственным, он работал бы так, как это делает DBNull.Value сегодня. Один из способов указать поставщику «Я хочу использовать значение по умолчанию» может состоять в том, чтобы вообще не объявлять параметр (параметр со значением по умолчанию, конечно, имеет смысл описать как «необязательный параметр»), но в сценарий, в котором объект команды кэшируется и используется повторно, это приводит к удалению и повторному добавлению параметра.

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

...