Exception lorsque le paramètre AddWithValue est NULL


89

J'ai le code suivant pour spécifier les paramètres de la requête SQL. Je reçois l'exception suivante lorsque j'utilise Code 1; mais fonctionne très bien quand je l'utilise Code 2. Dans Code 2nous avons un contrôle pour null et donc un if..elsebloc.

Exception:

La requête paramétrée '(@application_ex_id nvarchar (4000)) SELECT E.application_ex_id A' attend le paramètre '@application_ex_id', qui n'a pas été fourni.

Code 1 :

command.Parameters.AddWithValue("@application_ex_id", logSearch.LogID);

Code 2 :

if (logSearch.LogID != null)
{
         command.Parameters.AddWithValue("@application_ex_id", logSearch.LogID);
}
else
{
        command.Parameters.AddWithValue("@application_ex_id", DBNull.Value );
}

QUESTION

  1. Pouvez-vous s'il vous plaît expliquer pourquoi il est impossible de prendre NULL de la valeur logSearch.LogID dans le code 1 (mais capable d'accepter DBNull)?

  2. Existe-t-il un meilleur code pour gérer cela?

Référence :

  1. Attribuer null à un SqlParameter
  2. Le type de données renvoyé varie en fonction des données du tableau
  3. Erreur de conversion de la base de données smallint en C # nullable int
  4. Quel est l'intérêt de DBNull?

CODE

    public Collection<Log> GetLogs(LogSearch logSearch)
    {
        Collection<Log> logs = new Collection<Log>();

        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            connection.Open();

            string commandText = @"SELECT  *
                FROM Application_Ex E 
                WHERE  (E.application_ex_id = @application_ex_id OR @application_ex_id IS NULL)";

            using (SqlCommand command = new SqlCommand(commandText, connection))
            {
                command.CommandType = System.Data.CommandType.Text;

                //Parameter value setting
                //command.Parameters.AddWithValue("@application_ex_id", logSearch.LogID);
                if (logSearch.LogID != null)
                {
                    command.Parameters.AddWithValue("@application_ex_id", logSearch.LogID);
                }
                else
                {
                    command.Parameters.AddWithValue("@application_ex_id", DBNull.Value );
                }

                using (SqlDataReader reader = command.ExecuteReader())
                {
                    if (reader.HasRows)
                    {
                        Collection<Object> entityList = new Collection<Object>();
                        entityList.Add(new Log());

                        ArrayList records = EntityDataMappingHelper.SelectRecords(entityList, reader);

                        for (int i = 0; i < records.Count; i++)
                        {
                            Log log = new Log();
                            Dictionary<string, object> currentRecord = (Dictionary<string, object>)records[i];
                            EntityDataMappingHelper.FillEntityFromRecord(log, currentRecord);
                            logs.Add(log);
                        }
                    }

                    //reader.Close();
                }
            }
        }

        return logs;
    }

3
Que voulez-vous dire par mieux? Le code 2 est la manière correcte d'envoyer une valeur nulle à une base de données.
Phil Gan

Réponses:


148

Ennuyeux, n'est-ce pas.

Vous pouvez utiliser:

command.Parameters.AddWithValue("@application_ex_id",
       ((object)logSearch.LogID) ?? DBNull.Value);

Ou bien, utilisez un outil comme "dapper", qui fera tout ce désordre pour vous.

Par exemple:

var data = conn.Query<SomeType>(commandText,
      new { application_ex_id = logSearch.LogID }).ToList();

Je suis tenté d'ajouter une méthode à dapper pour obtenir le IDataReader... je ne sais pas encore si c'est une bonne idée.


1
Je pensais à une extension sur la Parameterspropriété - est-ce un Object?
Phil Gan

6
@Phil hmmm, oui ça l'est, et je vois ce que tu veux dire ... peutAddWithValueAndTreatNullTheRightDamnedWay(...)
Marc Gravell

1
@MarcGravell Pouvez-vous expliquer pourquoi il est impossible de prendre NULL de la valeur logSearch.LogID dans le code 1 (mais capable d'accepter DBNull)?
LCJ

18
@Lijo car nulldans une valeur de paramètre signifie "ne pas envoyer ce paramètre". Je soupçonne que c'était une mauvaise décision qui a simplement fait cuire au four dans Je pense En fait,. La plupart des DBNullétait une décision fondamentalement mauvaise qui a obtenu cuit dans: stackoverflow.com/a/9632050/23354
Marc Gravell

1
@tylerH à cause des règles de fonte nulles - qui peuvent s'affaiblir en C # 9
Marc Gravell

52

Je trouve plus facile d'écrire simplement une méthode d'extension pour le SqlParameterCollectionqui gère les valeurs nulles:

public static SqlParameter AddWithNullableValue(
    this SqlParameterCollection collection,
    string parameterName,
    object value)
{
    if(value == null)
        return collection.AddWithValue(parameterName, DBNull.Value);
    else
        return collection.AddWithValue(parameterName, value);
}

Ensuite, vous l'appelez simplement comme:

sqlCommand.Parameters.AddWithNullableValue(key, value);

la valeur peut être int ou int ?, string, bool or bool ?, DateTime ou Datetime? , etc ?
Kiquenet

3
J'ai lu la réponse de Marc et j'ai pensé "Je pense que je préfère simplement écrire une méthode d'extension pour la collection Parameters", puis j'ai fait défiler un cheveu ... (la bonne chose à propos d'une méthode d'extension est que je peux faire une seule recherche / remplacement après et toutes mes mises à jour de code sont terminées)
jleach

1
Excellente solution ... Les méthodes d'extension doivent être définies dans une classe statique. Comment: implémenter et appeler une méthode d'extension personnalisée
Chris Catignani

2
Peut-être que je me trompe (une sorte de débutant en C #) mais ne pourriez-vous pas le faire de manière plus concise comme ceci:return collection.AddWithValue(parameterName, value ?? DBNull.Value);
Tobias Feil

1
@TobiasFeil Oui, vous pouvez le faire aussi. C'est juste une question de goût.
AxiomaticNexus

4

Juste au cas où vous le feriez en appelant une procédure stockée: je pense que c'est plus facile à lire si vous déclarez une valeur par défaut sur le paramètre et ne l'ajoutez que si nécessaire.

SQL:

DECLARE PROCEDURE myprocedure
    @myparameter [int] = NULL
AS BEGIN

C #:

int? myvalue = initMyValue();
if (myvalue.hasValue) cmd.Parameters.AddWithValue("myparamater", myvalue);

0

un problème, autorisé avec Nécessairement défini SQLDbType

command.Parameters.Add("@Name", SqlDbType.NVarChar);
command.Parameters.Value=DBNull.Value

où SqlDbType.NVarChar vous tapez. Définir obligatoirement le type SQL.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.