Какой смысл в DBNull?
В.NET есть null
ссылка, которая используется везде, чтобы обозначить, что ссылка на объект пуста, а затем есть DBNull
, который используется драйверами базы данных (и немногими другими), чтобы обозначить... почти то же самое. Естественно, это создает много путаницы, и необходимо преобразовать процедуры преобразования и т. Д.
Так почему же авторы.NET решили сделать это? Для меня это не имеет смысла. Их документация также не имеет смысла:
Класс DBNull представляет несуществующее значение. Например, в базе данных столбец в строке таблицы может не содержать никаких данных. То есть считается, что столбец вообще не существует, а просто не имеет значения. Объект DBNull представляет несуществующий столбец. Кроме того, COM-взаимодействие использует класс DBNull, чтобы различать вариант VT_NULL, который указывает на несуществующее значение, и вариант VT_EMPTY, который указывает неуказанное значение.
Что это за хрень про "столбец не существует"? Столбец существует, у него просто нет значения для конкретной строки. Если бы его не было, я бы получил исключение, пытаясь получить доступ к определенной ячейке, а не DBNull
! Я могу понять необходимость различать VT_NULL
а также VT_EMPTY
но почему бы не сделать COMEmpty
класс вместо? Это было бы гораздо лучше вписаться во всю среду.NET.
Я что-то пропустил? Может кто-нибудь пролить свет почему DBNull
был изобретен и какие проблемы это помогает решить?
5 ответов
Дело в том, что в определенных ситуациях существует разница между значением базы данных, равным NULL, и значением.NET, равным NULL.
Например. Если вы используете ExecuteScalar (который возвращает первый столбец первой строки в наборе результатов) и вы получаете нулевое значение, это означает, что выполненный SQL не возвращал никаких значений. Если вы возвращаете DBNull, это означает, что значение было возвращено SQL, и оно было NULL. Вы должны быть в состоянии отличить.
Я собираюсь не согласиться с тенденцией здесь. Я пойду на запись:
Я не согласна что
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
значение это не отправлено... хорошо, вот идея: если вы не хотите, чтобы параметр был отправлен - не добавляйте его в коллекцию параметров - каждый ОРМ, который я могу придумать, прекрасно работает без необходимости или использования
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
уже существует и имеет гораздо лучшую поддержку языка и времени выполнения.
DbNull
представляет собой ящик без содержимого; null
указывает на несуществование коробки.
Вы используете DBNull для отсутствующих данных. Нуль в языке.NET означает, что для объекта / переменной нет указателя.
DBNull отсутствует данные: http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx
Влияние недостающих данных на статистику:
Есть некоторые различия между нулем 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 была хорошей идеей или нет, но многие люди не знают о вещах, которые я упомянул здесь, поэтому я решил, что стоит упомянуть.
Чтобы ответить на ваш вопрос, вы должны подумать, почему DBNull вообще существует?
DBNull необходим в узком случае использования. В противном случае это не так. Большинству людей DBNull никогда не нужен. Я никогда не позволяю вводить их в хранилища данных, которые я разрабатываю. У меня ВСЕГДА есть значение, поэтому мои данные никогда не имеют значения "
DBNull необходим в одном случае, который может вам понадобиться... если вы используете некоторые функции статистики SQL (например: медиана, среднее значение) ... они обрабатывают DBNull специально... посмотрите эти документы... некоторые функции не включают DBNull в общем количестве для статистики... например: (87 сумм / 127 всего) vs. (87 сумм / 117 итогов) .. разница в том, что 10 из этих значений столбца были DBNull... вы можете видеть, что это изменится результат статистики.
Мне не нужно проектировать свои базы данных с помощью DBNull. Если бы мне когда-либо понадобились статистические результаты, я бы явно изобрел или добавил столбец, такой как UserDidProvideValue, для этого одного элемента, который требует какой-то особой обработки, потому что он не существует (например, мое общее количество 117 было бы суммой полей, отмеченных UserDidProvideValue=true) ... ха-ха, лол - в моей следующей жизни в качестве правителя Вселенной, лол - DBNull никогда бы не позволили выйти из области SQL... весь мир программирования теперь обязан проверять все дважды... когда Было ли у вас когда-нибудь мобильное приложение, настольное приложение или веб-сайт, которым нужно было иметь "нулевое" целое число? - Никогда...