将 null 分配给 SqlParameter

Posted

技术标签:

【中文标题】将 null 分配给 SqlParameter【英文标题】:Assign null to a SqlParameter 【发布时间】:2011-06-01 03:41:16 【问题描述】:

下面的代码给出了一个错误——“没有从 DBnull 到 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;

【问题讨论】:

您需要将 AgeItem.AgeIndex 转换为我认为的对象...***.com/questions/202271/…(顺便说一句,为什么第三行末尾的==?) 【参考方案1】:

问题在于?: 运算符无法确定返回类型,因为您返回的要么是int 值,要么是DBNull 类型的值,它们不兼容。

您当然可以将 AgeIndex 的实例转换为 object 类型,这将满足 ?: 的要求。

您可以使用?? null-coalescing 运算符,如下所示

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

这是 MSDN documentation 对 ?: 运算符的引用,用于解释问题

first_expression 和 second_expression 的类型必须相同,或者必须存在从一种类型到另一种类型的隐式转换。

【讨论】:

为什么尝试将null转换为对象时没有抛出异常?我认为应该是AgeItem.AgeIndex as object @Niels Brinch,不会有例外,因为 null 是一个对象,只要您不尝试取消引用它,它就是完全合法的。然而,在这个例子中,它不是 null 被强制转换为对象,它是 DBNull.Value,它实际上是一个值类型。这 ??运算符说“如果 AgetItem.AgeIndex 为空,则返回 DBNull.Value,否则返回 AgeItem.AgeIndex”,然后将响应转换为对象。有关更多详细信息,请参阅空合并运算符。 msdn.microsoft.com/en-us/library/ms173224.aspx 从技术上讲,使用空合并运算符 ?? 的解决方案与使用常规三元 ?: 的解决方案相同 - 您仍然需要将 AgeItem.AgeIndex 强制转换为对象: planIndexParameter.Value = AgeItem.AgeIndex.HasValue ? (object)AgeItem.AgeIndex : DBNull.Value;. 如果您要使用正则三元组?: 进行特定类型的比较,那么强制转换整个表达式将不起作用。您必须像这样强制转换非 dbnull 参数:someID == 0 ? DBNull.Value : (object)someID 这是真的,但是如果您需要使用可为空的值作为函数的入口参数,结果会消耗 SqlParameter,如果它为 null,则您遇到错误,这种方式是行不通的,您应该只使用简单的 If-Else 方式。例如: sample.Text.Trim() != "" ? func(sample.Text) : DBNull.Value;不能作为 ?: 和 ??【参考方案2】:

The accepted answer 建议使用演员表。但是,大多数 SQL 类型都有一个特殊的 Null 字段,可以用来避免这种强制转换。

例如SqlInt32.Null“表示可以分配给这个SqlInt32类实例的DBNull。”

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

【讨论】:

这个建议看起来很有希望,所以我尝试了“System.Data.SqlTypes.SqlString.Null”,但它不起作用。它将“Null”('N'、'u'、'l'、'l')的实际字符串放入字段中,而不是将其留空并保留为 true(null)。但是,旧的 2010 年“接受的答案”使用 cast with (object) ?? DBNull.Value 工作正常。 (我使用的 ADO.NET 提供程序是 SQLite,但我不确定这是否会有所不同。)我建议其他人仔细测试 Brian 的提示,以确保 null 行为按预期工作。 @JasDev:我隐约记得在对一位高级用户(我认为是 Marc Gravell)的评论中描述了这个技巧,并被告知它只适用于 Microsoft SQL Server。 @JasDev 提供者将是它在 SQL Server 中的不同之处,因为 Brain point 已经出来了。 此答案仅用implicit.one 替换了对对象的显式强制转换。在示例代码中,exampleNoCast 被声明为对象,因此仍会发生强制转换为对象。如果像在 OP 的代码中一样,将值直接分配给也是对象类型的 SqlParameter.Value,那么您仍然会得到强制转换。 即使在 SQL Server 上,使用 DataTable/SqlBulkCopy 时似乎也会失败。【参考方案3】:

您需要在 SQLCommand 中将 DBNull.Value 作为空参数传递,除非在存储过程中指定了默认值(如果您正在使用存储过程)。最好的方法是在查询执行之前为任何缺少的参数分配DBNull.Value,然后foreach 将完成这项工作。

foreach (SqlParameter parameter in sqlCmd.Parameters)

    if (parameter.Value == null)
    
        parameter.Value = DBNull.Value;
    

否则改变这一行:

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

如下:

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

因为您不能在条件语句中使用不同类型的值,因为 DBNull 和 int 彼此不同。希望这会有所帮助。

【讨论】:

这个答案真的很好,因为它以各种可能的方式举例。我喜欢第一种方法,我通常使用 EF,但在这个要求中无法做到,它为我节省了很多时间。谢谢! foreach 循环对我来说非常有用!谢谢【参考方案4】:

用一行代码,试试这个:

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

【讨论】:

您也可以将AgeItem.AgeIndex 转换为对象:(object)AgeItem.AgeIndex。但是,是的,也更喜欢你的方法【参考方案5】:

试试这个:

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;

【讨论】:

【参考方案6】:

如果使用条件(三元)运算符,编译器需要在两种类型之间进行隐式转换,否则会出现异常。

因此,您可以通过将两者之一转换为 System.Object 来修复它:

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

但是由于结果不是很漂亮,而且你总是要记住这个转换,你可以使用这样的扩展方法来代替:

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;

那么你就可以使用这个简洁的代码了:

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

【讨论】:

【参考方案7】:

在我看来,更好的方法是使用 SqlCommand 类的 Parameters 属性:

public static void AddCommandParameter(SqlCommand myCommand)

    myCommand.Parameters.AddWithValue(
        "@AgeIndex",
        (AgeItem.AgeIndex== null) ? DBNull.Value : AgeItem.AgeIndex);

【讨论】:

但是如果值是DBNull.Value,ADO.NET 可能有点难以猜测可能是什么 SqlDbType ......这很方便 - 但有点危险。 ...【参考方案8】:

试试这个:

if (AgeItem.AgeIndex != null)

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

换句话说,如果参数为空,请不要将其发送到您的存储过程(当然,假设存储过程接受您的问题中隐含的空参数)。

【讨论】:

但是现在,您只是省略了一个参数 - 我非常怀疑存储过程会不会对此感到满意.... 很可能,调用将失败,并指出“没有提供参数 @AgeIndex 的值这是预期的”..... 哇。残酷的。如果未传递参数(@AgeIndex int = 0),只需将存储的过程写入默认值即可。无时无刻不在发生。客户端可以接受默认值,也可以通过传递参数来覆盖它。为什么投反对票?【参考方案9】:

考虑使用可用的 Nullable(T) 结构。它只会让你设置值,如果你有它们,你的 SQL Command 对象将识别可以为空的值并相应地处理,而你不会有任何麻烦。

【讨论】:

【参考方案10】:
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;

【讨论】:

【参考方案11】:

一个简单的扩展方法是:

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

【讨论】:

【参考方案12】:

我使用一个简单的方法来检查空值。

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

【讨论】:

那是条件逻辑倒退吗? DBNull.Value 应该在第一个吗? 当然是。固定的。谢谢。【参考方案13】:

我的代码,在实际项目中工作 在制作sql参数之前查看三元运算符 这对我来说是最好的方式,没有问题:

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

【讨论】:

【参考方案14】:

试试这样的:

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;

【讨论】:

【参考方案15】:
int? nullableValue = null;
object nullableValueDB

   get
       if(nullableValue==null)
          return DBNull.Value;
       else
          return (int)nullableValue;
   

我就是这样解决的。

【讨论】:

【参考方案16】:
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);

【讨论】:

【参考方案17】:

这就是我所做的......

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

【讨论】:

【参考方案18】:
            dynamic psd = DBNull.Value;

            if (schedule.pushScheduleDate > DateTime.MinValue)
            
                psd = schedule.pushScheduleDate;
            


            sql.DBController.RunGeneralStoredProcedureNonQuery("SchedulePush",
                     new string[]  "@PushScheduleDate",
                     new object[]  psd , 10, "PushCenter");

【讨论】:

以上是关于将 null 分配给 SqlParameter的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 pyspark 将 null 分配给配置单元表列

TypeScript——不能将类型“HTMLElement | null”分配给类型“HTMLElement”

TypeScript——不能将类型“HTMLElement | null”分配给类型“HTMLElement”

TypeScript——不能将类型“HTMLElement | null”分配给类型“HTMLElement”

无法将 Null 值分配给 Nullable Int32?通过绑定

是否有更优雅的形式将 NULL 分配给 InsertCommand 的 NVarChar?