Quel est le point de DBNull?
Dans .NET il y a le null
de référence, qui est utilisé partout pour indiquer qu'un objet de référence est vide, et puis il y a la DBNull
, qui est utilisé par les pilotes de base de données (et quelques autres) pour désigner... à peu près la même chose. Naturellement, cela crée beaucoup de confusion et de routines de conversion doivent être sortis, etc.
Alors, pourquoi l'original .NET les auteurs décident de faire cela? Pour moi, il n'a pas de sens. Leur documentation n'a pas de sens, soit:
La DBNull classe représente une inexistant valeur. Dans une base de données, par exemple, une colonne dans une ligne d'une table peut ne pas contenir toutes sortes de données que ce soit. Qui est, la colonne est considérée ne pas exister du tout, au lieu de se contenter de ne pas avoir une valeur. Un DBNull objet représente l'inexistante de la colonne. En outre, l'interopérabilité COM utilise le DBNull classe de distinguer entre une VT_NULL variante, ce qui indique une inexistant valeur, et un VT_EMPTY variante, ce qui indique une valeur quelconque.
C'est quoi cette merde sur une colonne "n'existant pas"? Il existe une colonne, il n'a tout simplement pas avoir une valeur pour la ligne particulière. Si elle n'existait pas, j'aurais une exception en essayant d'accéder à la cellule spécifique, pas une DBNull
! Je peux comprendre la nécessité de faire la distinction entre VT_NULL
et VT_EMPTY
, mais alors pourquoi ne pas faire un COMEmpty
classe à la place? Ce serait beaucoup plus lisible ajustement dans l'ensemble .NET framework.
Suis-je raté quelque chose? Quelqu'un peut-il éclairer pourquoi DBNull
a été inventé, et les problèmes qu'il aide à résoudre?
- Juste un autre point de données... DBI (Interface de Base de données) module dans le langage Perl n'ont pas un concept de DbNull. Lorsqu'une valeur est NULL dans la base de données, DBI représente comme le Perl "undef" qui est le Perl équivalent de "null" en C#. Si Perl a pris la position d'un "DbNull" concept n'est pas nécessaire et je n'ai pas entendu de tous les programmeurs Perl souhaitant qu'ils avaient DbNull.
- Je suis avec vous, @JoelFan - je ne vois pas de réel intérêt dans il soit
- Outre les réponses ci-dessous, le commentaire ci-dessous par @thomas-levesque est très important:
DBNull
est antérieure à l'introduction dans le .NET framework de véritables types nullables. Tant leur comportement est légèrement différent, doncDBNull
avait de séjour (à mon grand regret, mais c'est une autre histoire). - J'aime la question, mais je dois dire que la communauté est incohérent. Beaucoup de questions sur "pourquoi est-il de cette façon?" obtenir fermés que "pas question", surtout si la personne se permet d'expliquer pourquoi quelque chose semble n'avoir aucun sens. (J'ai essayé de demander ou se plaindre du manque de soutien approprié pour les types abstraits en exposant des services WCF en tant que services web et me suis tourné vers le bas tout de suite!)
Vous devez vous connecter pour publier un commentaire.
Le point est que, dans certaines situations, il y a une différence entre une base de données de la valeur null, et une .NET Null.
Par exemple. Si vous utilisez ExecuteScalar (qui renvoie à la première colonne de la première ligne du jeu de résultats) et vous obtenez une valeur null dos qui signifie que le SQL exécutées n'ont pas de retour de toutes les valeurs. Si vous obtenez DBNull de retour, il signifie une valeur a été renvoyée par le SQL et il était NULL. Vous devez être en mesure de faire la différence.
ExecuteScalar
pourrait avoir été réécrit différemment pour résoudre ce problème particulier (DBEmptyRowset
, anyone?)ExecuteScalar()
méthode vs varient tout le DB, des fournisseurs, des ensembles de données/Tables/Lignes/Colonnes, des contrôles liés aux données, et quoi d'autre pas? J'irais pour la première. À la fin de la journée, pour que le code BEAUCOUP plus cohérente. Et simple. Outre -ExecuteScalar
est l'un des moins les méthodes utilisées dans les fournisseurs de données. >90% des cas sont avec DataReader. Plus - pensez-vous vraiment qu'unbool ExecuteScalar(out object Value)
serait que incohérent?DBNull
et de la vieille Api sont là pour rester - trop de code s'appuie sur elle. 🙂out
paramètre pots. Mais là encore, C# ne gère pas les Tuples bien (un peu d'une omission de la langue, je pense)bool/out
est cohérent avec les dictionnaires, c'est pourquoi je l'ai choisi à l'origine. Alternativement, il pourrait juste retour d'unobject
, puis créez une valeur spéciale pour un ensemble de lignes vide, par opposition à une valeur spéciale pour une valeur vide. Ou peut-être retourner la valeur null, et ont une option deout bool
paramètre, car dans la plupart des cas, vous n'avez pas vraiment sur le vide-ensemble de lignes de cas.ExecuteScalar
pourrait également retourner uneIEnumerable<object>
avec un ou des valeurs nulles.ScalarResult
avec unHasValue
etValue
de la propriété. Il n'y a aucune raison valable pour beaucoup d'autres méthodes de retourDBNull
au lieu denull
. C'est juste une mauvaise conception.ExecuteScalar
lever une exception (DbException
) si il n'y avait pas une valeur de retour (pas de lignes, ou pas de colonnes), et utilisénull
au lieu deDBNull
ExecuteScalar
NULL signifie que pas de lignes ont été retournés, et DBNULL est qu'au moins une ligne a été retourné, mais la première colonne n'a pas de valeur.Je ne suis pas daccord avec la tendance ici. Je vais aller sur le dossier:
L'argument est souvent mis en avant que les
null
est une référence non valide, et queDBNull
est un objet nul motif; n'est pas vraie,. Par exemple:ce n'est pas une référence non valide"; c'est une
null
valeur. En effetnull
signifie tout ce que vous voulez dire, et franchement, je n'ai absolument aucun problème à travailler avec des valeurs qui peuvent êtrenull
(en effet, même dans SQL nous avons besoin pour travailler correctement avecnull
- rien ne change ici). De même, la "objet nul motif" n'a de sens que si vous êtes réellement en la traitant comme un objet en programmation orientée objet, mais si nous avons une valeur qui peut être "notre valeur, ou unDBNull
" ensuite, il doit êtreobject
, donc on ne peut pas être quelque chose d'utile avec elle.Il y a beaucoup de mauvaises choses avec
DBNull
:object
, puisque seulementobject
peut contenirDBNull
ou une autre valeurDBNull
" vs "pourrait être une valeur ounull
"null
parfaitement bien dans 1.1DBDataReader.IsDBNull
ouDataRow.IsNull
- ni réellement besoinDBNull
existe sur le plan de l'APIDBNull
échoue en termes de null-coalescence;value ?? defaultValue
ne fonctionne pas si la valeur estDBNull
DBNull.Value
ne peut pas être utilisé dans les paramètres facultatifs, car ce n'est pas une constanteDBNull
sont identiques à la sémantique denull
; en particulier,DBNull
fait est égal àDBNull
- il ne pas faire le travail de représentation de la sémantique SQLobject
DBNull
, vous pourriez aussi bien avoir testé juste pournull
null
valeur, il n'est pas envoyé... eh bien, en voilà une idée: si vous ne voulez pas un paramètre envoyé - ne pas l'ajouter à la collection de paramètresDBNull
, sauf comme un supplément de nuisance lorsque l'on parle à l'ADO.NET codeLa seule même à distance argument convaincant j'ai jamais vu pour justifier la existence d'une telle valeur est en
DataTable
, lors du passage en valeurs afin de créer une nouvelle ligne; unnull
signifie "utiliser par défaut", unDBNull
est explicitement null franchement cette API pourrait avoir eu un traitement spécifique pour ce cas - imaginaireDataRow.DefaultValue
par exemple serait beaucoup mieux que de l'introduction d'unDBNull.Value
qui infecte de vastes portions de code pour aucune raison.De même, la
ExecuteScalar
scénario est... au mieux précaire; si vous exécutez un scalaire méthode, vous attendre un résultat. Dans le scénario où il n'y a pas de lignes, de retournull
ne semble pas trop terrible. Si vous avez absolument besoin de lever l'ambiguïté entre "pas de lignes" et "une seule valeur null est retournée", il y a le lecteur de l'API.Ce navire a navigué il y a longtemps, et il est beaucoup trop tard pour le corriger. Mais! S'il vous plaît ne pas pense que tout le monde est d'accord que c'est une "évidence" de la chose. Beaucoup de développeurs ne pas voir de la valeur dans ce drôle de pli sur la BCL.
En fait je me demande si tout cela provient de deux choses:
Nothing
au lieu de quelque chose avec la valeur "null" dans VBif(value is DBNull)
syntaxe qui "ressemble à SQL", plutôt que de oh-so-trickyif(value==null)
Résumé:
Avoir 3 options (
null
,DBNull
, ou une valeur réelle) n'est utile que s'il existe un véritable exemple où vous avez besoin de lever l'ambiguïté entre les 3 cas différents. Je n'ai pas encore vu une occasion où j'ai besoin de représenter les deux "null" les états, de sorteDBNull
est entièrement redondant étant donné quenull
existe déjà et a beaucoup mieux la langue et à l'exécution.DBNull
certainement n'est pas égal à null; trybool x = DBNull.Value.Equals(DBNull.Value); bool y = DBNull.Value.Equals(null);
. Le point que j'essayais de faire ici est que si vous essayez de faire cela dans un conforme à la norme ANSI SQL base de données, vous trouverez quenull
n'est pas égale ànull
(mais en même temps,null
ne "pas égal"null
)Nothing
dans VB.Net est proche de la marque. Vb.Net'sNothing
n'est pas un équivalent direct de C#'snull
(c'est plus près de match pourdefault(T)
) et, par conséquent, ne peut pas remplir pour VB.Net chaque fonction que vous suggéreznull
permettrait de mieux accomplir vs DBNull.Valeur en C#. Mais, oui: DBNull.Valeur suce. J'aimerais voir l'équipe CLR mettre en œuvre une sorte de "conversion automatique", donc si vous passez null ado.net méthodes de l'api, où seule DBNull.La valeur est valide (ou le définir comme la valeur d'un paramètre, par exemple), il fait la conversion pour vous.object
, et pour les valeurs null--de-T). Oui, la valeur null est une valeur. Dans le cas de référence de type de variables, il est le tout-zéro de référence (bien que techniquement faible valeur non nulle références seront également interprétées comme nulle dans certains scénarios, en raison d'un détail de l'implémentation). Unobject
variable peut contenir une référence à une instance deDBNull
; ce dernier, peut-être, je n'ai abréger - mais je ne pense pas que le sens est perdu.DBNull
est redondante avecnull
parce que null, c'est le mal et un défaut de conception. Accordé, être forcé d'utiliserobject
est également un défaut de conception, mais il est inexact de dire que seuls lesobject
pourrait tenirDBNull
ou une autre valeur un type somme ferait l'affaire.object
et 2: après avoir brisé null-coalescence en échange de l'utilisation de laSystem.Data.SqlTypes
espace de noms. Ce n'est pas l'idéal (je préfère utiliser le construit en types), mais il fonctionne. Par exemple,int? example = null;SqlInt32 nocast = example ?? SqlInt32.Null;
à Partir d'un code de rédaction de point de vue, ce n'est pas vraiment beaucoup d'une victoire, mais au moins il est facile à lire.SqlClient
- qui je déconseille fortement dans le cas général. Il y a beaucoup, beaucoup de bonnes raisons pour utiliser nue ADO.NET types (non spécifique au fournisseur types) dans la mesure du possible.DbNull
représente une zone avec pas de contenu;null
indique la non-existence de la boîte.null
à elle.Vous utilisez DBNull pour les données manquantes. La valeur Null dans la .NET langue signifie qu'il n'y a pas de pointeur d'un objet/variable.
DBNull données manquantes: http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx
Les effets de l'absence de données sur les statistiques:
http://en.wikipedia.org/wiki/Missing_values
Nullable<int>
alors? C'est un type de valeur, pas de pointeurs. C'est peut-être une erreur alors?DBNull
" nous avons besoin d'utiliserobject
- c'est toujours le cas dans la version 2.0+;object
pouvez stockernull
parfaitement bien donc, même lors de l'utilisation deobject
, leDBNull.Value
est toujours redondantIl y a quelques différences entre un CLR nulle et une DBNull. Tout d'abord, la valeur null dans les bases de données relationnelles a différents "équivaut à" la sémantique: null n'est pas égal à null. CLR null EST égal à null.
Mais je soupçonne que la raison principale est la façon par laquelle le paramètre valeurs par défaut dans SQL server et la mise en œuvre du fournisseur.
Pour voir la différence, créer une procédure avec un paramètre qui a une valeur par défaut:
Bien structuré DAL code qui doit séparer de création de commande de l'utilisation (pour permettre à l'aide de la même commande plusieurs fois, par exemple pour appeler une procédure stockée à de nombreuses reprises de manière efficace). Écrire une méthode qui retourne un SqlCommand représentant la procédure ci-dessus:
Maintenant, si vous invoquez la commande sans paramètre de la @s paramètre, ou de définir sa valeur (CLR) est null, il utilisera par défaut la valeur "bonjour". En revanche, si vous définissez la valeur du paramètre DBNull.Valeur, il sera utilisé et de l'écho DbNull.Valeur.
Puisqu'il y a deux résultats différents à l'aide de CLR null ou à la base de la valeur null comme valeur de paramètre, vous ne pouvez pas représenter les deux cas avec un seul d'entre eux. Si CLR null devait être le seul, qu'il faudrait travailler à la manière dont DBNull.Valeur d'aujourd'hui. Un moyen d'indiquer au fournisseur "je veux utiliser la valeur par défaut" pourrait alors être de ne pas déclarer le paramètre (un paramètre avec une valeur par défaut bien sûr de sens que pour décrire comme un "paramètre facultatif"), mais dans un scénario où la commande objet est mis en cache et réutilisés, ce qui a suscité la suppression et re-ajout du paramètre.
Je ne suis pas sûr si je pense que DBNull était une bonne idée ou pas, mais beaucoup de gens ne sont pas conscients des choses que j'ai mentionnées ici, donc j'ai pensé qu'il vaut la peine de mentionner.