Attribuer null à un SqlParameter


189

Le code suivant donne une erreur - "Aucune conversion implicite de DBnull en 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;

4
Vous devez lancer AgeItem.AgeIndex en objet, je pense ... stackoverflow.com/questions/202271/… (btw, pourquoi le ==à la fin de la 3ème ligne?)
Greg

Réponses:


341

Le problème est que l' ?:opérateur ne peut pas déterminer le type de retour car vous retournez unint valeur ou une valeur de type DBNull, qui ne sont pas compatibles.

Vous pouvez bien sûr convertir l'instance d'AgeIndex en un type objectqui satisferait l' ?:exigence.

Vous pouvez utiliser l' ??opérateur de fusion nul comme suit

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

Voici une citation de la documentation MSDN pour l' ?:opérateur qui explique le problème

Soit le type de first_expression et second_expression doit être le même, soit une conversion implicite doit exister d'un type à l'autre.


Pourquoi n'y a-t-il aucune exception levée lors de la tentative de conversion de null en objet? Je pense que ça devrait êtreAgeItem.AgeIndex as object
Niels Brinch

@Niels Brinch, il n'y aurait pas d'exception car null est un objet et tant que vous n'essayez pas de le déréférencer, c'est parfaitement légal. Cependant, dans cet exemple, il n'est pas nul en cours de conversion en objet, il s'agit de DBNull.Value qui est en fait un type valeur. Le ?? L'opérateur dit «si AgetItem.AgeIndex est nul, alors renvoie DBNull.Value sinon returen AgeItem.AgeIndex», la réponse est convertie en objet. Voir l'opérateur de fusion nul pour plus de détails. msdn.microsoft.com/en-us/library/ms173224.aspx
Chris Taylor

3
Techniquement, votre solution en utilisant l'opérateur null coalescent ??est la même solution que si vous deviez utiliser le ternaire régulière ?:- vous avez encore besoin de plâtre AgeItem.AgeIndexà un objet: planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;.
newfurniturey

Si vous deviez utiliser le ternaire régulier ?:pour effectuer une comparaison spécifique au type, la conversion de l'expression entière ne fonctionnera pas. Vous devez transtyper le paramètre non-dbnull comme ceci:someID == 0 ? DBNull.Value : (object)someID
ingrédient_15939

C'est vrai, mais si vous avez besoin d'utiliser la valeur null-able comme paramètre d'entrée de la fonction, le résultat consomme SqlParameter et s'il est nul, vous obtenez une erreur de cette façon ne fonctionne pas et vous devez utiliser simplement la méthode If-Else simple. par exemple: sample.Text.Trim ()! = ""? func (sample.Text): DBNull.Value; ne fonctionnera pas comme?: et ??
QMaster

105

La réponse acceptée suggère d'utiliser un plâtre. Cependant, la plupart des types SQL ont un champ Null spécial qui peut être utilisé pour éviter cette conversion.

Par exemple, SqlInt32.Null«représente un DBNull qui peut être affecté à cette instance de la classe SqlInt32».

int? example = null;
object exampleCast = (object) example ?? DBNull.Value;
object exampleNoCast = example ?? SqlInt32.Null;

2
La suggestion avait l'air prometteuse alors j'ai essayé "System.Data.SqlTypes.SqlString.Null" mais cela ne fonctionne pas. Il place la chaîne réelle de "Null" ('N', 'u', 'l', 'l') dans le champ au lieu de le laisser vide avec true (null). Cependant, l'ancienne "réponse acceptée" de 2010 qui utilise le cast avec (objet) ?? DBNull.Value fonctionne correctement. (Le fournisseur ADO.NET que j'ai utilisé était SQLite, mais je ne suis pas sûr que cela fasse une différence.) Je suggère que d'autres testent attentivement le conseil de Brian pour s'assurer que le comportement nul fonctionne comme prévu.
JasDev

6
@JasDev: Je me souviens vaguement d'avoir décrit cette astuce dans un commentaire à un utilisateur de haute réputation (je pense que Marc Gravell) et m'être dit que cela ne fonctionne que sur Microsoft SQL Server.
Brian

@JasDev le fournisseur sera la différence que cela fonctionne dans SQL Server comme le souligne Brain.
Lankymart

Cette réponse remplace uniquement un cast explicite en objet par un implicit.one. Dans l'exemple de code, exampleNoCastest déclaré objet, de sorte que la conversion en objet se produit toujours. Si, comme dans le code de l'OP, la valeur est affectée directement à SqlParameter.Value qui est également de type object, alors vous obtenez toujours le cast.
Scott

31

Vous devez passer en DBNull.Valuetant que paramètre nul dans SQLCommand, sauf si une valeur par défaut est spécifiée dans la procédure stockée (si vous utilisez une procédure stockée). La meilleure approche consiste à attribuer DBNull.Valuetout paramètre manquant avant l'exécution de la requête, et le fait de suivre foreach fera l'affaire.

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

Sinon, modifiez cette ligne:

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

Comme suit:

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

Parce que vous ne pouvez pas utiliser différents types de valeurs dans une instruction conditionnelle, car DBNull et int sont différents l'un de l'autre. J'espère que cela aidera.


Cette réponse est vraiment sympa car elle donne des exemples de toutes les manières possibles. J'aime la première approche, j'utilise habituellement EF mais dans cette exigence je ne pourrais pas le faire et cela me fait gagner beaucoup de temps. Merci!
Leandro

23

Avec une ligne de code, essayez ceci:

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

5

Essaye ça:

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;

5

Si vous utilisez l'opérateur conditionnel (ternaire), le compilateur a besoin d'une conversion implicite entre les deux types, sinon vous obtenez une exception.

Vous pouvez donc résoudre ce problème en lançant l'un des deux vers System.Object:

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

Mais comme le résultat n'est pas vraiment joli et que vous devez toujours vous souvenir de ce casting, vous pouvez utiliser une telle méthode d'extension à la place:

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

Ensuite, vous pouvez utiliser ce code concis:

planIndexParameter.Value = AgeItem.AgeIndex.GetDBNullOrValue();

1

À mon avis, le meilleur moyen est de le faire avec la propriété Parameters de la classe SqlCommand :

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

Mais si la valeur est DBNull.Value, ADO.NET pourrait avoir un peu de mal à deviner quel SqlDbType cela pourrait être ........ c'est pratique - mais un peu dangereux ....
marc_s

1

Pensez à utiliser la structure Nullable (T) disponible. Cela vous permettra de définir des valeurs uniquement si vous les avez, et vos objets de commande SQL reconnaîtront la valeur Nullable et traiteront en conséquence sans tracas de votre part.


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

0

Essaye ça:

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

En d'autres termes, si le paramètre est nul, ne l'envoyez pas à votre proc stocké (en supposant, bien sûr, que le proc stocké accepte des paramètres nuls qui sont implicites dans votre question).


Mais maintenant, vous omettez simplement un paramètre - je doute fortement que la procédure stockée soit satisfaite de cela .... très probablement, l'appel échouera en indiquant "aucune valeur pour le paramètre @AgeIndex fournie qui était attendue" .... .
marc_s

Sensationnel. Dur. Il suffit d'écrire le processus stocké sur une valeur par défaut si le paramètre n'est pas passé (@AgeIndex int = 0). Arrive tout le temps. Le client peut accepter la valeur par défaut ou la remplacer en passant le paramètre. Pourquoi le vote négatif?
Flipster

0

essayez quelque chose comme ça:

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

0
int? nullableValue = null;
object nullableValueDB
{
   get{
       if(nullableValue==null)
          return DBNull.Value;
       else
          return (int)nullableValue;
   }
}

Je résous comme ça.


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

0

C'est ce que je fais simplement ...

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

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

0

Une méthode d'extension simple pour cela serait:

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

0

J'utilise une méthode simple avec une vérification nulle.

    public SqlParameter GetNullableParameter(string parameterName, object value)
    {
        if (value != null)
        {
            return new SqlParameter(parameterName, value);
        }
        else
        {
            return new SqlParameter(parameterName, DBNull.Value);
        }
    }

1
Est-ce que cette logique conditionnelle est à l'envers? DBNull.Value doit-il être dans le premier?
Mark Schultheiss

Sûrement. Fixé. Merci.
Zhi An

0

Mon code, travaillant dans un projet réel Regardez l'opérateur ternaire avant de définir le paramètre sql, c'est le meilleur moyen pour moi, sans problèmes:

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