Atribuir nulo a um SqlParameter

188

O código a seguir fornece um erro - "Nenhuma conversão implícita de DBnull para 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;
Relatividade
fonte
4
Você precisa converter AgeItem.AgeIndex para um objeto que eu acho ... stackoverflow.com/questions/202271/… (btw, por que ==o final da 3ª linha?) #
29210 Greg Greg

Respostas:

341

O problema é que o ?:operador não pode determinar o tipo de retorno porque você está retornando um intvalor ou um valor do tipo DBNull, que não é compatível.

Obviamente, é possível converter a instância do AgeIndex para um tipo objectque atenda ao ?:requisito.

Você pode usar o ??operador coalescente nulo da seguinte maneira

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

Aqui está uma citação da documentação do MSDN para o ?:operador que explica o problema

O tipo de primeira expressão e segunda expressão deve ser o mesmo ou uma conversão implícita deve existir de um tipo para o outro.

Chris Taylor
fonte
Por que não há nenhuma exceção lançada ao tentar converter nulo para objeto? Eu acho que deveria serAgeItem.AgeIndex as object
Niels Brinch
@ Niels Brinch, não haveria uma exceção, porque null é um objeto e, desde que você não tente desreferê-lo, é perfeitamente legal. No entanto, neste exemplo, não é nulo sendo convertido em objeto, é DBNull.Value que é realmente um tipo de valor. O ?? O operador diz 'se AgetItem.AgeIndex for nulo, em seguida, retorne DBNull.Value, caso contrário retorne AgeItem.AgeIndex', a resposta será convertida em objeto. Consulte Operador coalescente nulo para obter mais detalhes. msdn.microsoft.com/en-us/library/ms173224.aspx
Chris Taylor
3
Tecnicamente, a solução usando o operador coalescentes nulo ??é a mesma solução como se você fosse usar o ternário regulares ?:- você ainda precisa de elenco AgeItem.AgeIndexpara um objeto: planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;.
Newfurniturey
Se você usar o ternário regular ?:para fazer uma comparação específica de tipo, a conversão da expressão inteira não funcionará. Você deve converter o parâmetro non-dbnull da seguinte forma:someID == 0 ? DBNull.Value : (object)someID
ingrediente_15939
Isso é verdade, mas se você precisar usar um valor nulo como parâmetro de entrada da função resultante, consome SqlParameter e, se for nulo, terá um erro dessa forma, não funcionará e deverá usar apenas a maneira If-Else. por exemplo: sample.Text.Trim ()! = ""? func (sample.Text): DBNull.Value; não vai funcionar como?: e ??
QMaster 15/11
102

A resposta aceita sugere fazer uso de um elenco. No entanto, a maioria dos tipos SQL possui um campo Nulo especial que pode ser usado para evitar essa conversão.

Por exemplo, SqlInt32.Null"Representa um DBNull que pode ser atribuído a esta instância da classe SqlInt32."

int? example = null;
object exampleCast = (object) example ?? DBNull.Value;
object exampleNoCast = example ?? SqlInt32.Null;
Brian
fonte
2
A sugestão parecia promissora, então tentei "System.Data.SqlTypes.SqlString.Null", mas não funciona. Ele coloca a sequência real de "Nulo" ('N', 'u', 'l', 'l') no campo, deixando-a em branco com true (null). No entanto, a antiga "resposta aceita" de 2010 que usa o cast com (objeto) ?? DBNull.Value funciona corretamente. (O provedor do ADO.NET que usei era o SQLite, mas não tenho certeza se isso faz alguma diferença.) Sugiro que outras pessoas testem cuidadosamente a dica de Brian para garantir que o comportamento nulo esteja funcionando conforme o esperado.
JasDev
6
@ JasDev: Lembro-me vagamente de descrever esse truque em um comentário para um usuário de alta reputação (acho Marc Marcvell) e ter dito que ele só funciona no Microsoft SQL Server.
18715 Brian
@JasDev, o provedor será a diferença que funciona no SQL Server, conforme indicado pelo Brain.
precisa saber é o seguinte
Essa resposta substitui apenas uma conversão explícita no objeto por um implicit.one. No código de exemplo, exampleNoCasté declarado objeto, portanto, a conversão para objeto ainda ocorre. Se, como no código do OP, o valor for atribuído diretamente a SqlParameter.Value, que também é do tipo objeto, você ainda obtém a conversão.
Scott
31

Você precisa passar DBNull.Valuecomo um parâmetro nulo no SQLCommand, a menos que um valor padrão seja especificado no procedimento armazenado (se você estiver usando o procedimento armazenado). A melhor abordagem é atribuir DBNull.Valuequalquer parâmetro ausente antes da execução da consulta, e seguir a seguir fará o trabalho.

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

Caso contrário, altere esta linha:

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

Do seguinte modo:

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

Como você não pode usar tipos diferentes de valores na instrução condicional, como DBNull e int são diferentes um do outro. Espero que isso ajude.

ShahidAzim
fonte
Essa resposta é muito boa, porque exemplifica de todas as formas possíveis. Gosto da primeira abordagem, geralmente uso EF, mas neste requisito não foi possível e isso me poupa muito tempo. Obrigado!
Leandro
23

Com uma linha de código, tente o seguinte:

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

Tente o seguinte:

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;
deciclone
fonte
5

Se você usar o operador condicional (ternário), o compilador precisará de uma conversão implícita entre os dois tipos, caso contrário, você receberá uma exceção.

Assim, você pode corrigi-lo lançando um dos dois para System.Object:

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

Mas como o resultado não é muito bonito e você sempre deve se lembrar dessa transmissão, você pode usar esse método de extensão:

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

Então você pode usar este código conciso:

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

Na minha opinião, a melhor maneira é fazer isso com a propriedade Parameters da classe SqlCommand :

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

fonte
Mas se o valor for DBNull.Value, ADO.NET pode ter um pouco de um momento difícil adivinhar o que SqlDbType que poderia ser ........ este é conveniente - mas um pouco perigoso ....
marc_s
1

Considere usar a estrutura Nullable (T) disponível. Isso permitirá que você defina valores apenas se os tiver, e seus objetos de Comando SQL reconhecerão o valor anulável e serão processados ​​de acordo com isso sem problemas.

Kanwar Singh
fonte
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
fonte
0

Tente o seguinte:

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

Em outras palavras, se o parâmetro for nulo, simplesmente não o envie ao seu processo armazenado (supondo, é claro, que o processo armazenado aceite parâmetros nulos, o que está implícito na sua pergunta).

Flipster
fonte
Mas agora, você está apenas omitindo um parâmetro - duvido muito que o procedimento armazenado fique feliz com isso .... muito provavelmente, a chamada falhará afirmando "nenhum valor para o parâmetro @AgeIndex fornecido que era esperado" .... .
marc_s
Uau. Harsh. Basta escrever o processo armazenado como padrão para um valor se o parâmetro não for passado (@AgeIndex int = 0). Acontece o tempo todo. O cliente pode aceitar o padrão ou substituí-lo passando o parâmetro Por que o voto negativo?
Flipster
0

tente algo como isto:

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

Estou resolvendo assim.

Um Sinan Direk
fonte
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
fonte
0

Isto é o que eu simplesmente faço ...

        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
fonte
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
fonte
0

Um método de extensão simples para isso seria:

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

Eu uso um método simples com uma verificação nula.

    public SqlParameter GetNullableParameter(string parameterName, object value)
    {
        if (value != null)
        {
            return new SqlParameter(parameterName, value);
        }
        else
        {
            return new SqlParameter(parameterName, DBNull.Value);
        }
    }
Zhi An
fonte
1
Essa lógica é condicional ao contrário? DBNull.Value deve estar no primeiro?
Mark Schultheiss
Certamente é. Fixo. Obrigado.
Zhi An
0

Meu código, trabalhando em projeto real Olhe o operador ternário antes de fazer o parâmetro sql, este é o melhor caminho para mim, sem 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
fonte