Assegnare null a un SqlParameter


189

Il codice seguente riporta un errore: "Nessuna conversione implicita da DBnull a 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
Devi lanciare AgeItem.AgeIndex per obiettare, penso ... stackoverflow.com/questions/202271/… (a proposito, perché ==alla fine della 3a riga?)
Greg,

Risposte:


341

Il problema è che l' ?:operatore non è in grado di determinare il tipo restituito perché si sta restituendo un intvalore o un valore di tipo DBNull, che non sono compatibili.

Ovviamente è possibile eseguire il cast dell'istanza di AgeIndex in modo objectche corrisponda al ?:requisito.

È possibile utilizzare l' ??operatore a coalescenza nulla come segue

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

Ecco una citazione dalla documentazione MSDN per l' ?:operatore che spiega il problema

Il tipo di first_expression e second_expression deve essere lo stesso oppure deve esistere una conversione implicita da un tipo all'altro.


Perché non viene generata alcuna eccezione quando si tenta di eseguire il cast di null su oggetto? Penso che dovrebbe essereAgeItem.AgeIndex as object
Niels Brinch il

@Niels Brinch, non ci sarebbe un'eccezione perché null è un oggetto e fintanto che non provi a dereferenziarlo, è perfettamente legale. Tuttavia, in questo esempio, non è null essere cast su oggetto, è DBNull.Value che in realtà è un tipo di valore. Il ?? l'operatore dice "se AgetItem.AgeIndex è null, quindi restituisce DBNull.Value altrimenti ritorna a AgeItem.AgeIndex", quindi la risposta viene trasmessa all'oggetto. Vedere operatore di coalescenza null per maggiori dettagli. msdn.microsoft.com/en-us/library/ms173224.aspx
Chris Taylor

3
Tecnicamente, la soluzione utilizzando l'operatore null coalescenza ??è la stessa soluzione, come se si sceglie di usare il ternario normale ?:- è ancora necessario per fusione AgeItem.AgeIndexa un oggetto: planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;.
newfurniturey,

Se dovessi usare il ternario normale ?:per fare un confronto specifico del tipo, allora lanciare l'intera espressione non funzionerà. Devi lanciare il parametro non dbnull in questo modo:someID == 0 ? DBNull.Value : (object)someID
ingrediente_15939

Questo è vero, ma se hai bisogno di usare il valore null-grado come parametro di ingresso della funzione che risulta consuma SqlParameter e se è nullo hai un errore in questo modo non funziona e dovresti usare solo un semplice metodo If-Else. ad esempio: sample.Text.Trim ()! = ""? func (sample.Text): DBNull.Value; non funzionerà come?: e ??
QMaster,

105

La risposta accettata suggerisce di usare un cast. Tuttavia, la maggior parte dei tipi SQL ha uno speciale campo Null che può essere utilizzato per evitare questo cast.

Ad esempio, SqlInt32.Null"Rappresenta un DBNull che può essere assegnato a questa istanza della classe SqlInt32."

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

2
Il suggerimento sembrava promettente, quindi ho provato "System.Data.SqlTypes.SqlString.Null" ma non funziona. Mette la stringa effettiva di "Null" ('N', 'u', 'l', 'l') nel campo invece di lasciarlo vuoto con true (null). Tuttavia, la vecchia "risposta accettata" del 2010 che utilizza cast con (oggetto) ?? DBNull.Value funziona correttamente. (Il provider ADO.NET che ho usato era SQLite ma non sono sicuro che ciò faccia la differenza.) Suggerisco ad altri di testare attentamente il suggerimento di Brian per assicurarsi che il comportamento null funzioni come previsto.
JasDev

6
@JasDev: Ricordo vagamente di aver descritto questo trucco in un commento a un utente di alto livello (penso Marc Gravell) e che mi è stato detto che funziona solo su Microsoft SQL Server.
Brian,

@JasDev il provider sarà la differenza che funziona in SQL Server come sottolinea Brain.
Lankymart,

Questa risposta sostituisce solo un cast esplicito per obiettare con un implicit.one. Nel codice di esempio, exampleNoCastviene dichiarato oggetto, quindi il cast su oggetto si verifica ancora. Se, come nel codice OP, il valore viene assegnato direttamente a SqlParameter.Value che è anche di tipo oggetto, allora si ottiene comunque il cast.
Scott

31

È necessario passare DBNull.Valuecome parametro null all'interno di SQLCommand, a meno che non venga specificato un valore predefinito all'interno della procedura memorizzata (se si utilizza la procedura memorizzata). L'approccio migliore è quello di assegnare DBNull.Valuequalsiasi parametro mancante prima dell'esecuzione della query, e seguirà foreach farà il lavoro.

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

Altrimenti cambia questa riga:

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

Come segue:

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

Poiché non è possibile utilizzare diversi tipi di valori nell'istruzione condizionale, poiché DBNull e int sono diversi l'uno dall'altro. Spero che questo possa aiutare.


Questa risposta è davvero interessante perché mostra esempi in tutti i modi possibili. Mi piace il primo approccio, di solito uso EF ma in questo requisito non potrei farlo e mi fa risparmiare un sacco di tempo. Grazie!
Leandro,

23

Con una riga di codice, prova questo:

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

5

Prova questo:

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

Se si utilizza l'operatore condizionale (ternario), il compilatore necessita di una conversione implicita tra entrambi i tipi, altrimenti si ottiene un'eccezione.

Quindi potresti risolverlo lanciando uno di entrambi a System.Object:

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

Ma dal momento che il risultato non è davvero carino e devi sempre ricordare questo casting, puoi invece usare un metodo di estensione simile:

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;
}

Quindi è possibile utilizzare questo codice conciso:

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

1

Secondo me il modo migliore è farlo con la proprietà Parameters della classe SqlCommand :

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

Ma se il valore è DBNull.Value, ADO.NET potrebbe avere qualche difficoltà a indovinare quale SqlDbType potrebbe essere ........ questo è conveniente - ma un po 'pericoloso ....
marc_s

1

Prendi in considerazione l'utilizzo della struttura Nullable (T) disponibile. Ti consentirà di impostare i valori solo se li hai, e gli oggetti Comando SQL riconosceranno il valore nullable ed elaboreranno di conseguenza senza problemi da parte tua.


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

Prova questo:

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

In altre parole, se il parametro è null, non inviarlo al proc memorizzato (presupponendo, naturalmente, che il proc memorizzato accetti parametri null che sono impliciti nella tua domanda).


Ma ora stai solo omettendo un parametro - dubito fortemente che la procedura memorizzata sarà contenta di questo .... molto probabilmente, la chiamata fallirà affermando "nessun valore per il parametro @AgeIndex fornito che era previsto" .... .
marc_s

Wow. Harsh. Basta scrivere il proc memorizzato su un valore predefinito se il parametro non viene passato (@AgeIndex int = 0). Succede tutte le volte. Il client può accettare il valore predefinito o sovrascriverlo passando il parametro. Perché il downvote?
Flipster,

0

prova qualcosa del genere:

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;
   }
}

Sto risolvendo così.


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

Questo è quello che faccio semplicemente ...

        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

Un semplice metodo di estensione per questo sarebbe:

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

0

Uso un metodo semplice con un controllo null.

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

1
Quella logica condizionale è all'indietro? DBNull.Value dovrebbe essere nel primo?
Mark Schultheiss,

Sicuramente lo è. Fisso. Grazie.
Zhi Un

0

Il mio codice, lavorando in un vero progetto Guarda l'operatore ternario prima di rendere sqlparameter questo è il modo migliore per me, senza problemi:

    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);
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.