Asignar nulo a un parámetro Sql

189

El siguiente código da un error: "No hay conversión implícita de 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;
Relatividad
fuente
44
Necesitas lanzar AgeItem.AgeIndex para objetar, creo ... stackoverflow.com/questions/202271/… (por cierto, ¿por qué ==al final de la tercera línea?)
Greg

Respuestas:

341

El problema es que el ?:operador no puede determinar el tipo de retorno porque está devolviendo unint valor o un valor de tipo DBNull, que no son compatibles.

Por supuesto, puede emitir la instancia de AgeIndex para que sea de tipo object que satisfaga el ?:requisito.

Puede usar el ??operador de fusión nula de la siguiente manera

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

Aquí hay una cita de la documentación de MSDN para?: operador que explica el problema

El tipo de first_expression y second_expression debe ser el mismo, o debe existir una conversión implícita de un tipo a otro.

Chris Taylor
fuente
¿Por qué no se produce ninguna excepción al intentar convertir nulo en objeto? Creo que debería serAgeItem.AgeIndex as object
Niels Brinch
@Niels Brinch, no habría una excepción porque nulo es un objeto y siempre que no intentes desreferenciarlo, es perfectamente legal. Sin embargo, en este ejemplo no se convierte nulo en objeto, es DBNull.Value, que en realidad es un tipo de valor. Los ?? El operador dice 'si AgetItem.AgeIndex es nulo y luego devuelve DBNull.Value de lo contrario returen AgeItem.AgeIndex', entonces la respuesta se convierte en objeto. Ver operador de fusión nulo para más detalles. msdn.microsoft.com/en-us/library/ms173224.aspx
Chris Taylor
3
Técnicamente, su solución mediante el operador nulo coalescencia ??es la misma solución que si usted fuera a utilizar el ternario regulares ?:- usted todavía necesita fundido AgeItem.AgeIndexa un objeto: planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;.
muebles nuevos
Si tuviera que usar el ternario regular ?:para hacer una comparación específica de tipo, entonces emitir toda la expresión no funcionará. Debe emitir el parámetro no dbnull de la siguiente manera:someID == 0 ? DBNull.Value : (object)someID
ingrediente_15939
Eso es cierto, pero si necesita usar un valor nulo como parámetro de entrada de la función que resulta consumir SqlParameter y si es nulo, tiene un error de esta manera no funciona y debe usar la forma simple If-Else. por ejemplo: sample.Text.Trim ()! = ""? func (sample.Text): DBNull.Value; no funcionará como?: y ??
QMaster
105

La respuesta aceptada sugiere hacer uso de un elenco. Sin embargo, la mayoría de los tipos de SQL tienen un campo nulo especial que se puede utilizar para evitar esta conversión.

Por ejemplo, SqlInt32.Null"Representa un DBNull que se puede asignar a esta instancia de la clase SqlInt32".

int? example = null;
object exampleCast = (object) example ?? DBNull.Value;
object exampleNoCast = example ?? SqlInt32.Null;
Brian
fuente
2
La sugerencia parecía prometedora, así que probé "System.Data.SqlTypes.SqlString.Null" pero no funciona. Pone la cadena real de "Nulo" ('N', 'u', 'l', 'l') en el campo en lugar de dejarlo en blanco con verdadero (nulo). Sin embargo, la vieja "respuesta aceptada" de 2010 que usa el reparto con (objeto)? DBNull.Value funciona correctamente. (El proveedor de ADO.NET que utilicé fue SQLite, pero no estoy seguro de si eso hace la diferencia). Sugiero que otros prueben cuidadosamente la sugerencia de Brian para asegurarse de que el comportamiento nulo funcione como se esperaba.
JasDev
66
@JasDev: recuerdo vagamente que describí este truco en un comentario a un usuario de alta reputación (creo que Marc Gravell) y me dijeron que solo funciona en Microsoft SQL Server.
Brian
@JasDev, el proveedor será la diferencia, esto funciona en SQL Server como lo señala Brain.
Lankymart
Esta respuesta solo reemplaza una conversión explícita al objeto con un implícito. En el código de muestra, exampleNoCastse declara objeto, por lo que todavía se produce la conversión al objeto. Si, como en el código del OP, el valor se asigna directamente a SqlParameter.Value, que también es de tipo objeto, aún así obtiene el reparto.
Scott
31

DBNull.ValueDebe pasar como un parámetro nulo dentro de SQLCommand, a menos que se especifique un valor predeterminado dentro del procedimiento almacenado (si está utilizando un procedimiento almacenado). El mejor enfoque es asignar DBNull.Valuecualquier parámetro faltante antes de la ejecución de la consulta, y seguir foreach hará el trabajo.

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

De lo contrario, cambie esta línea:

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

Como sigue:

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

Porque no puede usar diferentes tipos de valores en la declaración condicional, ya que DBNull e int son diferentes entre sí. Espero que esto ayude.

ShahidAzim
fuente
Esta respuesta es realmente buena porque muestra ejemplos de todas las formas posibles. Me gusta el primer enfoque, generalmente uso EF pero en este requisito no pude hacerlo y me ahorró mucho tiempo. ¡Gracias!
Leandro
23

Con una línea de código, intente esto:

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

Prueba esto:

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;
deciclón
fuente
5

Si utiliza el operador condicional (ternario), el compilador necesita una conversión implícita entre ambos tipos, de lo contrario obtendrá una excepción.

Para que pueda solucionarlo lanzando uno de los dos a System.Object:

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

Pero dado que el resultado no es realmente bonito y siempre debes recordar este casting, puedes usar un método de extensión de este tipo:

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

Entonces puedes usar este código conciso:

planIndexParameter.Value = AgeItem.AgeIndex.GetDBNullOrValue();
Tim Schmelter
fuente
1

En mi opinión, la mejor manera es hacer esto con la propiedad Parameters de la clase SqlCommand :

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

fuente
Pero si el valor es DBNull.Value, ADO.NET podría tener un poco de dificultad para adivinar lo que podría ser SqlDbType ........ esto es conveniente - pero un poco peligroso ....
marc_s
1

Considere usar la estructura Nullable (T) disponible. Solo le permitirá establecer valores si los tiene, y sus objetos de comando SQL reconocerán el valor anulable y procesarán en consecuencia sin problemas de su parte.

Kanwar Singh
fuente
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;
}
Anil Kumar
fuente
0

Prueba esto:

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

En otras palabras, si el parámetro es nulo, simplemente no lo envíe a su proceso almacenado (suponiendo, por supuesto, que el proceso almacenado acepte parámetros nulos, lo cual está implícito en su pregunta).

Flipster
fuente
Pero ahora, solo está omitiendo un parámetro; dudo mucho que el procedimiento almacenado esté contento con esto ... lo más probable es que la llamada falle diciendo "no se proporcionó ningún valor para el parámetro @AgeIndex que se esperaba" ... .
marc_s
Guau. Duro. Simplemente escriba el proceso almacenado como predeterminado en un valor si el parámetro no se pasa (@AgeIndex int = 0). Pasa todo el tiempo. El cliente puede aceptar el valor predeterminado o anularlo pasando el parámetro. ¿Por qué el voto negativo?
Flipster
0

prueba algo como esto:

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;
}
usuario2574441
fuente
0
int? nullableValue = null;
object nullableValueDB
{
   get{
       if(nullableValue==null)
          return DBNull.Value;
       else
          return (int)nullableValue;
   }
}

Estoy resolviendo así.

Un sinan direk
fuente
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);
Anil Kumar
fuente
0

Esto es lo que simplemente hago ...

        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();
Tom Mack
fuente
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");
papapa
fuente
0

Un método de extensión simple para esto sería:

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

Yo uso un método simple con un cheque nulo.

    public SqlParameter GetNullableParameter(string parameterName, object value)
    {
        if (value != null)
        {
            return new SqlParameter(parameterName, value);
        }
        else
        {
            return new SqlParameter(parameterName, DBNull.Value);
        }
    }
Zhi An
fuente
1
¿Es esa lógica condicional al revés? ¿Debería DBNull.Value estar en el primero?
Mark Schultheiss
Seguramente lo es. Fijo. Gracias.
Zhi An
0

Mi código, trabajando en un proyecto real Mira el operador ternario antes de hacer el parámetro sql, esta es la mejor manera para mí, sin problemas:

    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);
    }
Ángel Ibáñez
fuente