2009-05-14 10 views
22

Ho una query sql che ha un parametro che può essere nullo nel database (Sql Server). Il metodo di aggiornamento funziona correttamente finché l'utente non inserisce uno spazio vuoto nel campo, producendo un valore null per l'oggetto DataTime (questo oggetto è annullabile). Il problema è quando il dbCommand.ExecuteNonQuery();.SqlParameter con valore Nullable genera un errore mentre ExecuteNonQuery?

Ecco come costruisco il parametro per questo campo:

IDataParameter dbParam_au_id = new SqlParameter(); 
    dbParam_au_id.ParameterName = "@birthday"; 
    dbParam_au_id.Value = birthday; 
    dbParam_au_id.DbType = DbType.DateTime; 
    dbCommand.Parameters.Add(dbParam_au_id); 

devo cercare di convertire il valore nullo di compleanno per DBNull.Value così:

IDataParameter dbParam_au_id = new SqlParameter(); 
    dbParam_au_id.ParameterName = "@birthday"; 
    dbParam_au_id.Value = birthday??DBNull.Value; 
    dbParam_au_id.DbType = DbType.DateTime; 
    dbCommand.Parameters.Add(dbParam_au_id); 

Ma questo codice won Compilare e ottenere errore:

Errore 1 Operatore '??' non può essere applicato agli operandi di tipo 'System.DateTime?' e 'System.DBNull'

Qualche idea?

+0

In una nota a margine, raccomando caldamente di non impostare la proprietà DbType a meno che non la si utilizzi come parametro di output. Non ho mai visto innumerevoli piccoli bug causati da e non uno solo risolto usando. –

risposta

51

I tipi non sono compatibili. Provare qualcosa di simile:

dbParam_au_id.Value = (object)birthday ?? DBNull.Value; 
13

Se la classe SqlParameter è stato scritto correttamente la prima volta ... un valore C# nulla sarebbe stato gestito come DBNull.Value. Ciò sarebbe intuitivo, quindi l'impostazione DI COURSE di un valore SqlParameter su null è funzionalmente equivalente alla rimozione da SqlParameterCollection.

Per correggere questo ridicolo errore di progettazione dell'API, creare il proprio metodo AddParameter (con overload), che accetta uno SqlParameterCollection, una stringa (nome parametro) e un oggetto (valore parametro).

#region Add by Name/Value. 
/// <summary> 
/// Adds an input parameter with a name and value. Automatically handles conversion of null object values to DBNull.Value. 
/// </summary> 
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param> 
/// <param name="name">The name of the parameter to add.</param> 
/// <param name="value">The value of the parameter to add.</param> 
private static void AddParameter(SqlParameterCollection parameters, string name, object value) 
{ 
    parameters.Add(new SqlParameter(name, value ?? DBNull.Value)); 
} 

/// <summary> 
/// Adds a parameter with a name and value. You specify the input/output direction. Automatically handles conversion of null object values to DBNull.Value. 
/// </summary> 
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param> 
/// <param name="name">The name of the parameter to add.</param> 
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param> 
/// <param name="direction">The ParameterDirection of the parameter to add (input, output, input/output, or return value).</param> 
private static void AddParameter(SqlParameterCollection parameters, string name, object value, ParameterDirection direction) 
{ 
    SqlParameter parameter = new SqlParameter(name, value ?? DBNull.Value); 
    parameter.Direction = direction; 
    parameters.Add(parameter); 
} 
#endregion 

#region Add by Name, Type, and Value. 
/// <summary> 
/// Adds an input parameter with a name, type, and value. Automatically handles conversion of null object values to DBNull.Value. 
/// </summary> 
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param> 
/// <param name="name">The name of the parameter to add.</param> 
/// <param name="type">Specifies the SqlDbType of the parameter.</param> 
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param> 
private static void AddParameter(SqlParameterCollection parameters, string name, SqlDbType type, object value) 
{ 
    AddParameter(parameters, name, type, 0, value ?? DBNull.Value, ParameterDirection.Input); 
} 

/// <summary> 
/// Adds a parameter with a name, type, and value. You specify the input/output direction. Automatically handles conversion of null object values to DBNull.Value. 
/// </summary> 
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param> 
/// <param name="name">The name of the parameter to add.</param> 
/// <param name="type">Specifies the SqlDbType of the parameter.</param> 
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param> 
/// <param name="direction">The ParameterDirection of the parameter to add (input, output, input/output, or return value).</param> 
private static void AddParameter(SqlParameterCollection parameters, string name, SqlDbType type, object value, ParameterDirection direction) 
{ 
    AddParameter(parameters, name, type, 0, value ?? DBNull.Value, direction); 
} 
#endregion 

#region Add by Name, Type, Size, and Value. 
/// <summary> 
/// Adds an input parameter with a name, type, size, and value. Automatically handles conversion of null object values to DBNull.Value. 
/// </summary> 
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param> 
/// <param name="name">The name of the parameter to add.</param> 
/// <param name="type">Specifies the SqlDbType of the parameter.</param> 
/// <param name="size">Specifies the size of the parameter for parameter types of variable size. Set to zero to use the default size.</param> 
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param> 
private static void AddParameter(SqlParameterCollection parameters, string name, SqlDbType type, int size, object value) 
{ 
    AddParameter(parameters, name, type, size, value ?? DBNull.Value, ParameterDirection.Input); 
} 

/// <summary> 
/// Adds a parameter with a name, type, size, and value. You specify the input/output direction. Automatically handles conversion of null object values to DBNull.Value. 
/// </summary> 
/// <param name="parameters">SqlParameterCollection from an SqlCommand instance.</param> 
/// <param name="name">The name of the parameter to add.</param> 
/// <param name="type">Specifies the SqlDbType of the parameter.</param> 
/// <param name="size">Specifies the size of the parameter for parameter types of variable size. Set to zero to use the default size.</param> 
/// <param name="value">The value of the parameter to add. If null, this is automatically converted to DBNull.Value.</param> 
/// <param name="direction">The ParameterDirection of the parameter to add (input, output, input/output, or return value).</param> 
private static void AddParameter(SqlParameterCollection parameters, string name, SqlDbType type, int size, object value, ParameterDirection direction) 
{ 
    SqlParameter parameter; 
    if (size < 1) 
     parameter = new SqlParameter(name, type); 
    else 
     parameter = new SqlParameter(name, type, size); 
    parameter.Value = value ?? DBNull.Value; 
    parameter.Direction = direction; 
    parameters.Add(parameter); 
} 
#endregion 

Come si può vedere, all'interno di quel metodo (e sovraccarichi), dove il valore è già digitato come oggetto, ho utilizzare l'istruzione "Valore ?? DBNull.Value" per far rispettare il nulla = DBNull.Value regola.

Ora, quando si passano riferimenti a oggetti null o tipi nullable senza valori al metodo AddParameter, si ottiene il comportamento previsto e intuitivo in cui un DBNull.Value viene passato alla query.

Non riesco a immaginare perché l'API sia stata implementata così com'è, perché se volessi che un parametro fosse ignorato, non lo AGGIUNGEREi e quindi IMPOSTA il suo valore su null. Io NON lo aggiungeremo o, in primo luogo, lo rimuoverò da SqlParameterCollection. Se aggiungo un parametro e SET il suo valore (anche se impostato su null), mi aspetto che venga utilizzato nella query, mi aspetto che null significhi valore nullo.

Ho sentito dire che non l'hanno implementato in modo "corretto" per motivi di prestazioni, ma è ridicolo, come dimostrato, perché chiamare il metodo SqlParameterCollection.AddWithValue converte tutto su un oggetto in ogni caso e convertire un'istanza di Nullable senza il valore di un oggetto nullo è una parte intrinseca del linguaggio C# che non è affatto un colpo di prestazioni. Microsoft dovrebbe davvero risolvere questo problema.

+1

bel set di funzioni di utilità. Se si è in 3.5 o versione successiva, è possibile regolarli come estensioni di SqlParameterCollection e si avrà command.Paramters.AddParameter (...) experience – Roman

+0

Sette anni più tardi, questo è ancora il comportamento del metodo Add SqlParameterCollection.Usi ancora questa soluzione? Mi chiedo solo se nel frattempo sia arrivata una soluzione migliore. Attualmente utilizzo AddRange con un array di parametri, quindi sto osservando il dover scorrere l'array e sostituire il valore di ognuno se null ... – Doug

Problemi correlati