In .NET gibt es die null
Referenz, die überall verwendet wird, um anzuzeigen, dass eine Objektreferenz leer ist, und dann die DBNull
, die von Datenbanktreibern (und wenigen anderen) verwendet wird, um ... so ziemlich dasselbe zu bezeichnen. Dies führt natürlich zu großer Verwirrung und Konvertierungsroutinen müssen durchgeführt werden usw.
Warum haben sich die ursprünglichen .NET-Autoren dazu entschlossen? Für mich macht es keinen Sinn. Ihre Dokumentation macht auch keinen Sinn:
Die DBNull-Klasse repräsentiert einen nicht vorhandenen Wert. In einer Datenbank enthält beispielsweise eine Spalte in einer Zeile einer Tabelle möglicherweise überhaupt keine Daten. Das heißt, die Spalte wird als überhaupt nicht vorhanden angesehen, anstatt lediglich keinen Wert zu haben. Ein DBNull-Objekt repräsentiert die nicht vorhandene Spalte. Darüber hinaus verwendet COM Interop die DBNull-Klasse, um zwischen einer VT_NULL-Variante, die einen nicht vorhandenen Wert angibt, und einer VT_EMPTY-Variante, die einen nicht angegebenen Wert angibt, zu unterscheiden.
Was ist das für ein Mist an einer "nicht existierenden Spalte"? Eine Spalte existiert, sie hat nur keinen Wert für die bestimmte Zeile. Wenn es nicht existieren würde, würde ich eine Ausnahme bekommen, die versucht, auf die spezifische Zelle zuzugreifen, nicht eine DBNull
! Ich kann die Notwendigkeit verstehen, zwischen VT_NULL
und zu unterscheiden VT_EMPTY
, aber warum dann nicht COMEmpty
stattdessen eine Klasse machen ? Das würde viel besser in das gesamte .NET-Framework passen.
Vermisse ich etwas Kann jemand etwas Licht ins Dunkel bringen, warum DBNull
es erfunden wurde und welche Probleme es zu lösen hilft?
DBNull
vor der Einführung von echten nullbaren Typen in das .NET-Framework. Beide verhalten sich etwas anders undDBNull
mussten bleiben (zu meinem Bedauern, aber das ist eine andere Geschichte).Antworten:
Der Punkt ist, dass es in bestimmten Situationen einen Unterschied zwischen einem Datenbankwert, der null ist, und einem .NET-Null gibt.
Zum Beispiel. Wenn Sie ExecuteScalar verwenden (das die erste Spalte der ersten Zeile in der Ergebnismenge zurückgibt) und eine Null zurückerhalten, bedeutet dies, dass die ausgeführte SQL keine Werte zurückgegeben hat. Wenn Sie DBNull zurückerhalten, bedeutet dies, dass ein Wert von SQL zurückgegeben wurde und NULL war. Sie müssen in der Lage sein, den Unterschied zu erkennen.
quelle
ExecuteScalar
hätte anders umgeschrieben werden können, um dieses spezielle Problem zu lösen (DBEmptyRowset
irgendjemand?)ExecuteScalar()
Methode und variieren Sie alle DB-Anbieter, DataSets / Tables / Rows / Columns, datengebundene Steuerelemente und was nicht? Ich würde für den ersten gehen. Letztendlich würde dies den gesamten Code VIEL konsistenter machen. Und einfach. Außerdem -ExecuteScalar
ist eine der am wenigsten verwendeten Methoden in den Datenanbietern. > 90% der Fälle betreffen DataReader. Plus - glaubst du wirklich, dass abool ExecuteScalar(out object Value)
so inkonsistent wäre?ExecuteScalar
, eine Ausnahme (DbException
) auszulösen, wenn es keinen zurückzugebenden Wert gäbe (keine Zeilen oder keine Spalten) undnull
stattdessenDBNull
ExecuteScalar
NULL bedeutet, dass keine Zeilen zurückgegeben wurden, und DBNULL bedeutet, dass mindestens eine Zeile zurückgegeben wurde, die erste Spalte jedoch keinen Wert hat.Ich werde dem Trend hier nicht zustimmen. Ich werde aufzeichnen:
Oft wird das Argument vorgebracht, dass
null
es sich um eine ungültige Referenz undDBNull
ein Nullobjektmuster handelt. beides ist nicht wahr . Zum Beispiel:int? x = null;
Dies ist keine "ungültige Referenz". es ist ein
null
Wert. In der Tatnull
bedeutet dies, was immer Sie wollen, und ehrlich gesagt habe ich absolut kein Problem damit, mit Werten zu arbeiten, die möglicherweise vorhanden sindnull
(sogar in SQL müssen wir korrekt arbeitennull
- hier ändert sich nichts). Ebenso ist das "Null-Objektmuster" nur dann sinnvoll, wenn Sie es tatsächlich als Objekt in OOP-Begriffen behandeln. Wenn wir jedoch einen Wert haben, der "unser Wert oder einDBNull
" sein kann, muss dies der Fall seinobject
, damit wir dies nicht können etwas Nützliches damit machen.Es gibt so viele schlechte Dinge mit
DBNull
:object
, da nur oder ein anderer Wertobject
halten kannDBNull
DBNull
" vs "könnte ein Wert sein odernull
"null
in 1.1 perfekt gebrauchenDBDataReader.IsDBNull
oderDataRow.IsNull
- von denen keine tatsächlichDBNull
in Bezug auf die API existieren mussDBNull
scheitert an der Nullverschmelzung;value ?? defaultValue
funktioniert nicht, wenn der Wert istDBNull
DBNull.Value
kann nicht in optionalen Parametern verwendet werden, da es keine Konstante istDBNull
ist identisch mit der Semantik vonnull
; Insbesondere ist esDBNull
tatsächlich gleichDBNull
- es erledigt also nicht die Aufgabe, die SQL-Semantik darzustellenobject
DBNull
, können Sie genauso gut nur testennull
null
Wert hat, nicht gesendet wird ... nun, hier ist eine Idee: Wenn Sie keinen Parameter senden möchten - nicht Fügen Sie es der Parametersammlung hinzuDBNull
, außer als zusätzliches Ärgernis, wenn ich mit dem ADO.NET-Code sprecheDas einzige , auch nur annähernd überzeugendes Argument habe ich jemals gesehen das rechtfertigen Existenz eines solchen Wert ist in
DataTable
, wenn in Werte vorbei eine neue Zeile zu erstellen; anull
bedeutet "Standard verwenden", aDBNull
ist explizit eine Null - offen gesagt hätte diese API eine spezielle Behandlung für diesen Fall haben können - ein imaginäresDataRow.DefaultValue
Beispiel wäre viel besser als die Einführung einesDBNull.Value
, das große Codebereiche ohne Grund infiziert.Ebenso ist das
ExecuteScalar
Szenario ... bestenfalls dürftig; Wenn Sie eine skalare Methode ausführen, erwarten Sie ein Ergebnis. In dem Szenario, in dem keine Zeilen vorhanden sind,null
scheint die Rückkehr nicht allzu schrecklich. Wenn Sie unbedingt zwischen "keine Zeilen" und "eine einzige zurückgegebene Null" unterscheiden müssen, gibt es die Reader-API.Dieses Schiff ist schon lange gesegelt und es ist viel zu spät, um es zu reparieren. Aber! Bitte denken Sie nicht , dass alle zustimmen, dass dies eine "offensichtliche" Sache ist. Viele Entwickler sehen keinen Wert in dieser seltsamen Falte in der BCL.
Ich frage mich tatsächlich, ob all dies auf zwei Dinge zurückzuführen ist:
Nothing
anstelle von etwas verwenden zu müssen, das "null" in VB beinhaltetif(value is DBNull)
Syntax, die "genau wie SQL aussieht", und nicht die ach so kniffligeif(value==null)
Zusammenfassung:
Mit 3 Optionen (
null
,DBNull
oder ein Ist - Wert) ist nur dann sinnvoll , wenn es ein echtes Beispiel ist , wo Sie zwischen drei verschiedenen Fällen müssen eindeutig zu machen. Ich habe noch keine Gelegenheit gesehen, bei der ich zwei verschiedene "Null" -Zustände darstellen muss. DaherDBNull
ist dies völlig überflüssig, da esnull
bereits existiert und eine viel bessere Sprach- und Laufzeitunterstützung bietet.quelle
DBNull
definitiv nicht gleich null; versuche esbool x = DBNull.Value.Equals(DBNull.Value); bool y = DBNull.Value.Equals(null);
. Der Punkt, den ich hier ansprechen wollte, ist, dass, wenn Sie dies in einer ANSI-kompatiblen SQL-Datenbank versuchen, Sie feststellen, dassnull
dies nicht gleich istnull
(aber gleichzeitignull
nicht "nicht gleich"null
)DbNull
stellt eine Box ohne Inhalt dar;null
zeigt das Nichtvorhandensein der Box an.quelle
null
.Sie verwenden DBNull für fehlende Daten. Null in der .NET-Sprache bedeutet, dass es keinen Zeiger für ein Objekt / eine Variable gibt.
DBNull fehlende Daten: http://msdn.microsoft.com/en-us/library/system.dbnull.value.aspx
Die Auswirkungen fehlender Daten auf die Statistik:
http://en.wikipedia.org/wiki/Missing_values
quelle
Nullable<int>
dann? Es ist ein Werttyp, keine Zeiger beteiligt. Vielleicht ist es dann ein Fehler?DBNull
" zu speichern, den wir verwenden müssenobject
- das bleibt in 2.0+ der Fall;object
kannnull
perfekt gut gespeichert werden - so dass auch bei Verwendungobject
dieDBNull.Value
noch überflüssig istEs gibt einige Unterschiede zwischen einer CLR-Null und einer DBNull. Erstens hat null in relationalen Datenbanken eine andere "gleiche" Semantik: null ist nicht gleich null. CLR null ist gleich null.
Ich vermute jedoch, dass der Hauptgrund in der Funktionsweise der Standardwerte der Parameter in SQL Server und der Implementierung des Anbieters liegt.
Um den Unterschied zu erkennen, erstellen Sie eine Prozedur mit einem Parameter, der einen Standardwert hat:
CREATE PROC [Echo] @s varchar(MAX) = 'hello' AS SELECT @s [Echo]
Gut strukturierter DAL-Code sollte die Befehlserstellung von der Verwendung trennen (um die mehrfache Verwendung desselben Befehls zu ermöglichen, z. B. um eine gespeicherte Prozedur mehrmals effizient aufzurufen). Schreiben Sie eine Methode, die einen SqlCommand zurückgibt, der die obige Prozedur darstellt:
SqlCommand GetEchoProc() { var cmd = new SqlCommand("Echo"); cmd.Parameters.Add("@s", SqlDbType.VarChar); return cmd; }
Wenn Sie den Befehl jetzt aufrufen, ohne den Parameter @s zu setzen, oder seinen Wert auf (CLR) null setzen, wird der Standardwert 'hello' verwendet. Wenn Sie andererseits den Parameterwert auf DBNull.Value setzen, wird dieser verwendet und DbNull.Value wiedergegeben.
Da es zwei unterschiedliche Ergebnisse gibt, bei denen CLR null oder database null als Parameterwert verwendet werden, können Sie nicht beide Fälle mit nur einem von ihnen darstellen. Wenn CLR null die einzige sein sollte, müsste es so funktionieren wie DBNull.Value heute. Eine Möglichkeit, dem Anbieter anzuzeigen, dass ich den Standardwert verwenden möchte, könnte darin bestehen, den Parameter überhaupt nicht zu deklarieren (ein Parameter mit einem Standardwert ist natürlich sinnvoll, ihn als "optionalen Parameter" zu beschreiben), sondern in a In einem Szenario, in dem das Befehlsobjekt zwischengespeichert und wiederverwendet wird, wird der Parameter entfernt und erneut hinzugefügt.
Ich bin mir nicht sicher, ob ich DBNull für eine gute Idee halte oder nicht, aber viele Leute wissen nichts von den Dingen, die ich hier erwähnt habe, und ich fand es erwähnenswert.
quelle
Um Ihre Frage zu beantworten, müssen Sie sich überlegen, warum es DBNull überhaupt gibt.
DBNull ist in einem engen Anwendungsfall erforderlich. Sonst ist es nicht. Die meisten Leute brauchen nie DBNull. Ich erlaube niemals, dass sie in Datenspeicher eingegeben werden, die ich entwerfe. Ich habe IMMER einen Wert, daher sind meine Daten niemals "<null>", ich wähle immer einen Typ mit aussagekräftigem 'Standardwert' und ich muss diese absurde Doppelprüfung niemals zweimal im Code durchführen, einmal für ist das Objekt null, und wieder für ist es DBNull, bevor ich mein Objekt in meinen tatsächlichen Datentyp (wie Int usw.) umwandle.
DBNull ist in einem Fall erforderlich, den Sie möglicherweise benötigen ... wenn Sie einige der SQL-Statistikfunktionen verwenden (z. B. Median, Durchschnitt). Sie behandeln DBNull speziell. Sehen Sie sich diese Dokumente an. Einige Funktionen enthalten kein DBNull in der Gesamtzahl für die Statistik ... zB: (87 Summe / 127 Summe) vs. (87 Summe / 117 Summe) .. Der Unterschied besteht darin, dass 10 dieser Spaltenwerte DBNull waren ... Sie können sehen, dass sich dies ändern würde das statistische Ergebnis.
Ich muss meine Datenbanken nicht mit DBNull entwerfen. Wenn ich jemals statistische Ergebnisse benötigte, würde ich explizit eine Spalte wie 'UserDidProvideValue' für dieses eine Element erfinden oder hinzufügen, das eine spezielle Behandlung benötigt, weil es nicht existiert (z. B. wäre meine Summe von 117 die Summe der markierten Felder UserDidProvideValue = true) ... haha lol - in meinem nächsten Leben als Herrscher des Universums lol - DBNull hätte niemals den SQL-Bereich verlassen dürfen ... die gesamte Programmierwelt ist jetzt belastet, alles zweimal zu überprüfen ... wann Hatten Sie jemals eine mobile App, eine Desktop-App oder eine Website, für die eine Ganzzahl "null" erforderlich ist? - Noch nie...
quelle
null
Wert in DB. Tatsächlich wünschte ich mir manchmal, ich könnte mehr als einen speziellen Wert definieren.null
Wert durch Aufteilen einer Spalte in zwei entfernt werden kann - einer würde "Null" (und vielleicht sogar andere spezielle Werte, wie ich oben wollte) und die anderen tatsächlichen Daten (die nur verwendet werden, wenn "Null" verwendet wird) enthalten. istfalse
). Aber wäre das besser? Ich bin mir nicht sicher ... mein erster Instinkt besagt, dassIFs
der Code undCASEs
das SQL viel, viel mehr enthalten würden, da ich jetzt zwei Spalten anstelle einer betrachten muss. Und ich würde vieleCHECK
Einschränkungen benötigen , um die Datenintegrität sicherzustellen.