Excepción cuando el parámetro AddWithValue es NULL

89

Tengo el siguiente código para especificar parámetros para la consulta SQL. Recibo la siguiente excepción cuando uso Code 1; pero funciona bien cuando lo uso Code 2. En Code 2tenemos una comprobación de nulo y, por tanto, un if..elsebloque.

Excepción:

La consulta parametrizada '(@application_ex_id nvarchar (4000)) SELECT E.application_ex_id A' espera el parámetro '@application_ex_id', que no se proporcionó.

Codigo 1 :

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

Codigo 2 :

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

PREGUNTA

  1. ¿Puede explicar por qué no puede tomar NULL del valor logSearch.LogID en el Código 1 (pero puede aceptar DBNull)?

  2. ¿Existe un código mejor para manejar esto?

Referencia :

  1. Asignar nulo a un parámetro Sql
  2. El tipo de datos devuelto varía según los datos de la tabla
  3. Error de conversión de la base de datos smallint a C # nullable int
  4. ¿Cuál es el punto de DBNull?

CÓDIGO

    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;
    }
LCJ
fuente
3
¿Qué quieres decir con mejor? El código 2 es la forma correcta de enviar un valor nulo a una base de datos.
Phil Gan

Respuestas:

148

Molesto, ¿no es así?

Puedes usar:

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

O alternativamente, use una herramienta como "dapper", que hará todo ese lío por usted.

Por ejemplo:

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

Estoy tentado de agregar un método a Dapper para obtener el IDataReader... aún no estoy seguro de si es una buena idea.

Marc Gravell
fuente
1
Estaba pensando en una extensión de la Parameterspropiedad, ¿es eso Object?
Phil Gan
6
@Phil hmmm, sí lo es, y veo lo que quieres decir ... tal vezAddWithValueAndTreatNullTheRightDamnedWay(...)
Marc Gravell
1
@MarcGravell ¿Puede explicar por qué no puede tomar NULL del valor logSearch.LogID en el Código 1 (pero puede aceptar DBNull)?
LCJ
18
@Lijo porque nullen un parámetro el valor significa "no enviar este parámetro". Sospecho que fue una mala decisión que simplemente se incorporó. De hecho, creo que la mayor parte DBNullfue una decisión fundamentalmente mala que se preparó
Marc Gravell
1
@tylerH debido a las reglas de fundición de carbón nulo, que pueden estar debilitándose en C # 9
Marc Gravell
52

Me resulta más fácil escribir un método de extensión para el SqlParameterCollectionque maneja valores nulos:

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

Entonces simplemente lo llamas como:

sqlCommand.Parameters.AddWithNullableValue(key, value);
AxiomaticNexus
fuente
el valor puede ser int o int ?, string, bool o bool ?, DateTime o Datetime? , etc?
Kiquenet
3
Leí la respuesta de Marc y pensé: "Creo que prefiero escribir un método de extensión para la colección de parámetros", luego me desplacé hacia abajo ... (lo bueno de un método de extensión es que puedo buscar / reemplazar después y todas mis actualizaciones de código están hechas)
jleach
1
Gran solución ... Los métodos de extensión deben definirse en una clase estática. Cómo: implementar y llamar a un método de extensión personalizado
Chris Catignani
2
Tal vez estoy equivocado (una especie de novato en C #) pero no podrías hacerlo de manera más concisa así:return collection.AddWithValue(parameterName, value ?? DBNull.Value);
Tobias Feil
1
@TobiasFeil Sí, tú también podrías hacer eso. Es solo cuestión de gustos.
AxiomaticNexus
4

En caso de que esté haciendo esto mientras llama a un procedimiento almacenado: creo que es más fácil de leer si declara un valor predeterminado en el parámetro y lo agrega solo cuando sea necesario.

SQL:

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

C#:

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

algún problema, permitido con necesariamente establecido SQLDbType

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

donde escribe SqlDbType.NVarChar. Establecer necesariamente el tipo de SQL.

usuario1599225
fuente