将 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的主要内容,如果未能解决你的问题,请参考以下文章
TypeScript——不能将类型“HTMLElement | null”分配给类型“HTMLElement”
TypeScript——不能将类型“HTMLElement | null”分配给类型“HTMLElement”
TypeScript——不能将类型“HTMLElement | null”分配给类型“HTMLElement”