Weisen Sie einem SqlParameter null zu


188

Der folgende Code gibt einen Fehler aus: "Keine implizite Konvertierung von DBnull nach int."

SqlParameter[] parameters = new SqlParameter[1];    
SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);
planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex;
parameters[0] = planIndexParameter;

4
Sie müssen AgeItem.AgeIndex umwandeln, um Einwände zu erheben, glaube ich ... stackoverflow.com/questions/202271/… (übrigens, warum ==am Ende der 3. Zeile?)
Greg

Antworten:


341

Das Problem besteht darin, dass der ?:Operator den Rückgabetyp nicht ermitteln kann, da Sie entweder einen intWert oder einen Wert vom Typ DBNull zurückgeben, die nicht kompatibel sind.

Sie können die Instanz von AgeIndex natürlich als Typ umwandeln, objectder die ?:Anforderung erfüllt.

Sie können den ??Null-Koaleszenz-Operator wie folgt verwenden

SqlParameter[] parameters = new SqlParameter[1];     
SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);
planIndexParameter.Value = (object)AgeItem.AgeIndex ?? DBNull.Value;
parameters[0] = planIndexParameter; 

Hier ist ein Zitat aus der MSDN-Dokumentation für den ?:Bediener, das das Problem erklärt

Entweder müssen der Typ des ersten und des zweiten Ausdrucks identisch sein, oder es muss eine implizite Konvertierung von einem Typ zum anderen vorhanden sein.


Warum wird beim Versuch, null in ein Objekt umzuwandeln, keine Ausnahme ausgelöst? Ich würde denken, es sollte seinAgeItem.AgeIndex as object
Niels Brinch

@Niels Brinch, es würde keine Ausnahme geben, da null ein Objekt ist und solange Sie nicht versuchen, es zu dereferenzieren, ist es vollkommen legal. In diesem Beispiel wird jedoch nicht null in ein Objekt umgewandelt, sondern DBNull.Value ist tatsächlich ein Werttyp. Das ?? Der Operator sagt: "Wenn AgetItem.AgeIndex null ist, geben Sie DBNull.Value zurück. Andernfalls wird AgeItem.AgeIndex zurückgegeben." Die Antwort wird dann in ein Objekt umgewandelt. Weitere Informationen finden Sie unter Null-Koaleszenz-Operator. msdn.microsoft.com/en-us/library/ms173224.aspx
Chris Taylor

3
Technisch gesehen ist Ihre Lösung mit dem Null-Koaleszenz-Operator ??dieselbe Lösung, als ob Sie das reguläre Ternär verwenden würden ?:- Sie müssen immer noch AgeItem.AgeIndexein Objekt umwandeln : planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;.
Newfurniturey

Wenn Sie das reguläre Ternär verwenden ?:, um einen typspezifischen Vergleich durchzuführen , funktioniert das Umwandeln des gesamten Ausdrucks nicht. Sie haben die nicht-DBNull Parameter wie so würfensomeID == 0 ? DBNull.Value : (object)someID
ingredient_15939

Das ist wahr, aber wenn Sie einen nullfähigen Wert als Eingabeparameter für die Funktion verwenden müssen, verbraucht das Ergebnis SqlParameter, und wenn es null ist, haben Sie einen Fehler. Diese Methode funktioniert nicht und Sie sollten nur eine einfache If-Else-Methode verwenden. Zum Beispiel: sample.Text.Trim ()! = ""? func (sample.Text): DBNull.Value; wird nicht funktionieren als ?: und ??
QMaster

102

Die akzeptierte Antwort schlägt vor, eine Besetzung zu verwenden. Die meisten SQL-Typen verfügen jedoch über ein spezielles Nullfeld, mit dem diese Umwandlung vermieden werden kann.

Beispiel: SqlInt32.Null"Stellt einen DBNull dar, der dieser Instanz der SqlInt32-Klasse zugewiesen werden kann."

int? example = null;
object exampleCast = (object) example ?? DBNull.Value;
object exampleNoCast = example ?? SqlInt32.Null;

2
Der Vorschlag sah vielversprechend aus, also habe ich "System.Data.SqlTypes.SqlString.Null" ausprobiert, aber es funktioniert nicht. Es fügt die tatsächliche Zeichenfolge "Null" ('N', 'u', 'l', 'l') in das Feld ein und lässt sie stattdessen mit true (null) leer. Allerdings ist die alte 2010 "akzeptierte Antwort", die Besetzung mit (Objekt) verwendet? DBNull.Value funktioniert ordnungsgemäß. (Der von mir verwendete ADO.NET-Anbieter war SQLite, aber ich bin mir nicht sicher, ob dies einen Unterschied macht.) Ich schlage vor, dass andere Brians Tipp sorgfältig testen, um sicherzustellen, dass das Nullverhalten wie erwartet funktioniert.
JasDev

6
@JasDev: Ich erinnere mich vage daran, diesen Trick in einem Kommentar an einen Benutzer mit hohen Repräsentanten (ich glaube Marc Gravell) beschrieben zu haben und erfahren zu haben, dass er nur unter Microsoft SQL Server funktioniert.
Brian

@JasDev der Anbieter wird der Unterschied sein, der in SQL Server funktioniert, wie Brain hervorhebt.
Lankymart

Diese Antwort ersetzt nur eine explizite Umwandlung in ein Objekt durch eine implizite. Im Beispielcode exampleNoCastwird als Objekt deklariert, sodass die Umwandlung in ein Objekt weiterhin erfolgt. Wenn der Wert wie im OP-Code direkt SqlParameter.Value zugewiesen wird, der ebenfalls vom Typ object ist, erhalten Sie trotzdem die Umwandlung.
Scott

31

Sie müssen DBNull.Valuein SQLCommand als Nullparameter übergeben werden, es sei denn, in der gespeicherten Prozedur ist ein Standardwert angegeben (wenn Sie eine gespeicherte Prozedur verwenden). Der beste Ansatz besteht darin, DBNull.Valuefehlende Parameter vor der Ausführung der Abfrage zuzuweisen. Wenn Sie foreach folgen, wird der Job ausgeführt.

foreach (SqlParameter parameter in sqlCmd.Parameters)
{
    if (parameter.Value == null)
    {
        parameter.Value = DBNull.Value;
    }
}

Andernfalls ändern Sie diese Zeile:

planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex;

Wie folgt:

if (AgeItem.AgeIndex== null)
    planIndexParameter.Value = DBNull.Value;
else
    planIndexParameter.Value = AgeItem.AgeIndex;

Da Sie in bedingten Anweisungen keine unterschiedlichen Wertetypen verwenden können, unterscheiden sich DBNull und int voneinander. Hoffe das wird helfen.


Diese Antwort ist wirklich nett, weil sie in jeder Hinsicht beispielhaft ist. Ich mag den ersten Ansatz, ich benutze normalerweise EF, aber in dieser Anforderung konnte ich es nicht und es spart mir viel Zeit. Vielen Dank!
Leandro

23

Versuchen Sie Folgendes mit einer Codezeile:

var piParameter = new SqlParameter("@AgeIndex", AgeItem.AgeIndex ?? (object)DBNull.Value);

5

Versuche dies:

SqlParameter[] parameters = new SqlParameter[1];    
SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);

planIndexParameter.IsNullable = true; // Add this line

planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex== ;
parameters[0] = planIndexParameter;

5

Wenn Sie den bedingten (ternären) Operator verwenden, benötigt der Compiler eine implizite Konvertierung zwischen beiden Typen, andernfalls erhalten Sie eine Ausnahme.

Sie können das Problem beheben, indem Sie eines von beiden auf Folgendes übertragen System.Object:

planIndexParameter.Value = (AgeItem.AgeIndex== null) ? DBNull.Value : (object) AgeItem.AgeIndex;

Da das Ergebnis jedoch nicht wirklich hübsch ist und Sie sich immer an dieses Casting erinnern müssen, können Sie stattdessen eine solche Erweiterungsmethode verwenden:

public static object GetDBNullOrValue<T>(this T val)
{
    bool isDbNull = true;
    Type t = typeof(T);

    if (Nullable.GetUnderlyingType(t) != null)
        isDbNull = EqualityComparer<T>.Default.Equals(default(T), val);
    else if (t.IsValueType)
        isDbNull = false;
    else
        isDbNull = val == null;

    return isDbNull ? DBNull.Value : (object) val;
}

Dann können Sie diesen kurzen Code verwenden:

planIndexParameter.Value = AgeItem.AgeIndex.GetDBNullOrValue();

1

Meiner Meinung nach ist es besser, dies mit der Parameters- Eigenschaft der SqlCommand- Klasse zu tun :

public static void AddCommandParameter(SqlCommand myCommand)
{
    myCommand.Parameters.AddWithValue(
        "@AgeIndex",
        (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex);
}

Aber wenn der Wert ist DBNull.Value, könnte es ADO.NET etwas schwer fallen zu erraten, welcher SqlDbType das sein könnte ........ dies ist praktisch - aber ein bisschen gefährlich ....
marc_s

1

Erwägen Sie die Verwendung der verfügbaren Nullable (T) -Struktur. Sie können nur Werte festlegen, wenn Sie diese haben, und Ihre SQL-Befehlsobjekte erkennen den nullbaren Wert und verarbeiten ihn entsprechend, ohne dass Sie Probleme damit haben.


1
if (_id_categoria_padre > 0)
{
    objComando.Parameters.Add("id_categoria_padre", SqlDbType.Int).Value = _id_categoria_padre;
}
else
{
    objComando.Parameters.Add("id_categoria_padre", DBNull.Value).Value = DBNull.Value;
}

0

Versuche dies:

if (AgeItem.AgeIndex != null)
{
   SqlParameter[] parameters = new SqlParameter[1];
   SqlParameter planIndexParameter = new SqlParameter("@AgeIndex", SqlDbType.Int);
   planIndexParameter.Value = AgeItem.AgeIndex;
   parameters[0] = planIndexParameter;
}

Mit anderen Worten, wenn der Parameter null ist, senden Sie ihn einfach nicht an Ihren gespeicherten Prozess (vorausgesetzt natürlich, dass der gespeicherte Prozess null Parameter akzeptiert, was in Ihrer Frage impliziert ist).


Aber jetzt lassen Sie nur einen Parameter weg - ich bezweifle sehr, dass sich die gespeicherte Prozedur darüber freuen wird. Höchstwahrscheinlich schlägt der Aufruf fehl und gibt an, dass "kein Wert für den Parameter @AgeIndex angegeben wurde, der erwartet wurde". .
marc_s

Beeindruckend. Hart. Schreiben Sie einfach den gespeicherten Prozess standardmäßig auf einen Wert, wenn der Parameter nicht übergeben wird (@AgeIndex int = 0). Es passiert die ganze Zeit. Der Client kann die Standardeinstellung entweder akzeptieren oder durch Übergeben des Parameters überschreiben. Warum das Downvote?
Flipster

0

versuchen Sie so etwas:

if (_id_categoria_padre > 0)
{
    objComando.Parameters.Add("id_categoria_padre", SqlDbType.Int).Value = _id_categoria_padre;
}
else
{
    objComando.Parameters.Add("id_categoria_padre", DBNull.Value).Value = DBNull.Value;
}

0
int? nullableValue = null;
object nullableValueDB
{
   get{
       if(nullableValue==null)
          return DBNull.Value;
       else
          return (int)nullableValue;
   }
}

Ich löse so.


0
if (AgeItem.AgeIndex== null)  
    cmd.Parameters.Add(new SqlParameter("ParaMeterName", SqlDbType.DateTime).Value = DBNull);  
else  
    cmd.Parameters.Add(new SqlParameter("ParaMeterName", SqlDbType.DateTime).Value = AgeItem.AgeIndex);

0

Das mache ich einfach ...

        var PhoneParam = new SqlParameter("@Phone", DBNull.Value);
        if (user.User_Info_Phone != null)
        {
            PhoneParam.SqlValue = user.User_Info_Phone;
        }

        return this.Database.SqlQuery<CustLogonDM>("UpdateUserInfo @UserName, @NameLast, @NameMiddle, @NameFirst, @Address, @City, @State, @PostalCode, @Phone",
            UserNameParam, NameLastParam, NameMiddleParam, NameFirstParam, AddressParam, CityParam, StateParam, PostalParam, PhoneParam).Single();

0
            dynamic psd = DBNull.Value;

            if (schedule.pushScheduleDate > DateTime.MinValue)
            {
                psd = schedule.pushScheduleDate;
            }


            sql.DBController.RunGeneralStoredProcedureNonQuery("SchedulePush",
                     new string[] { "@PushScheduleDate"},
                     new object[] { psd }, 10, "PushCenter");

0

Eine einfache Erweiterungsmethode hierfür wäre:

    public static void AddParameter(this SqlCommand sqlCommand, string parameterName, 
        SqlDbType sqlDbType, object item)
    {
        sqlCommand.Parameters.Add(parameterName, sqlDbType).Value = item ?? DBNull.Value;
    }

0

Ich benutze eine einfache Methode mit einer Nullprüfung.

    public SqlParameter GetNullableParameter(string parameterName, object value)
    {
        if (value != null)
        {
            return new SqlParameter(parameterName, value);
        }
        else
        {
            return new SqlParameter(parameterName, DBNull.Value);
        }
    }

1
Ist diese bedingte Logik rückwärts? Sollte DBNull.Value im ersten sein?
Mark Schultheiss

Sicher ist. Fest. Vielen Dank.
Zhi An

0

Mein Code, der in einem realen Projekt arbeitet Schauen Sie sich den ternären Operator an, bevor Sie den SQL-Parameter erstellen. Dies ist der beste Weg für mich, ohne Probleme:

    public bool Key_AddExisting
    (
          string clave
        , int? idHito_FileServer
        , int? idTipoDocumental_Almacen
        , string tipoExp_CHJ
        , int idTipoExp_Verti2
        , int idMov_Verti2
    )
    {
        List<SqlParameter> pars = new List<SqlParameter>()
        {
              new SqlParameter { ParameterName = "@Clave", Value = clave }
    LOOK -> , idHito_FileServer == null ? new SqlParameter { ParameterName = "@IdHito_FileServer", Value = DBNull.Value } : new SqlParameter { ParameterName = "@IdHito_FileServer", Value = idHito_FileServer }
    LOOK -> , idTipoDocumental_Almacen == null ? new SqlParameter { ParameterName = "@IdTipoDocumental_Almacen", Value = DBNull.Value } : new SqlParameter { ParameterName = "@IdTipoDocumental_Almacen", Value = idTipoDocumental_Almacen }
            , new SqlParameter { ParameterName = "@TipoExp_CHJ", Value = tipoExp_CHJ }
            , new SqlParameter { ParameterName = "@IdTipoExp_Verti2", Value = idTipoExp_Verti2 }
            , new SqlParameter { ParameterName = "@IdMov_Verti2", Value = idMov_Verti2 }
        };

        string sql = "INSERT INTO [dbo].[Enlaces_ClavesCHJ_MovimientosVerti2] " +
            "( " +
            "  [Clave] " +
            ", [IdHito_FileServer] " +
            ", [IdTipoDocumental_Almacen] " +
            ", [TipoExp_CHJ] " +
            ", [IdTipoExp_Verti2] " +
            ", [IdMov_Verti2] " +
            ") " +
            "VALUES" +
            "( " +
            "  @Clave" +
            ", @IdHito_FileServer" +
            ", @IdTipoDocumental_Almacen" +
            ", @TipoExp_CHJ" +
            ", @IdTipoExp_Verti2" +
            ", @IdMov_Verti2" +
            ")";

        return DbBasic.ExecNonQuery(ref this.conn, sql, pars);
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.