C# 总是将DBNull SqlParameter键入int是否安全?
最近,我们遇到了以下问题:C# 总是将DBNull SqlParameter键入int是否安全?,c#,sql-server,ado.net,C#,Sql Server,Ado.net,最近,我们遇到了以下问题: 简而言之:myCommand.Parameters.AddWithValue(“@SomeParameter”,DBNull.Value)显然将DBNull参数“类型”为nvarchar,它可以隐式转换为几乎所有其他类型,但不幸的是,不是varbinary,从而产生以下错误: 不允许从数据类型nvarchar隐式转换为varbinary(max)。使用CONVERT函数运行此查询 遗憾的是,链接问题中建议的解决方案不适用,因为我们在数据访问库的深处使用了AddW
myCommand.Parameters.AddWithValue(“@SomeParameter”,DBNull.Value)
显然将DBNull参数“类型”为nvarchar,它可以隐式转换为几乎所有其他类型,但不幸的是,不是varbinary
,从而产生以下错误:
不允许从数据类型nvarchar隐式转换为varbinary(max)。使用CONVERT函数运行此查询
遗憾的是,链接问题中建议的解决方案不适用,因为我们在数据访问库的深处使用了AddWithValue
,该库旨在从参数类型推断数据类型,不支持添加“真实”SQL Server类型
我通过显式地将DBNull参数输入为int
s“修复”了这个问题:
void MyImprovedAddWithValue(SqlCommand cmd, string key, object value)
{
if (value == DBNull.Value)
{
cmd.Parameters.Add(key, SqlDbType.Int).Value = DBNull.Value;
}
else
{
cmd.Parameters.AddWithValue(key, value);
}
}
这似乎奏效了。显然,作为int类型的NULL可以隐式转换为SQL Server支持的任何其他类型
忽略这样一个事实(我们非常清楚),使用这种方法是否会产生任何问题?
SqlParameterCollection.AddWithValue
的设计者是否有充分的理由不“默认”这样做?我忽略了这一点。AddWithValue
有更多的问题。存在的问题是,它创建了一个类型为NVARCHAR(X)
的参数,其中X
是值的确切长度。由于NVARCHAR(73)
与NVARCHAR(74)
的类型不同,SQL Server查询计划缓存会被许多相同的计划所污染,这些计划只在参数类型上有所不同。另一个问题是,当比较涉及VARCHAR
类型的列,并且NVARCHAR
类型的参数导致类型转换,而不是取消对所述列使用索引的资格
有太多的理由反对
AddWithValue
,我从未使用过它,也从未推荐过它,而且总是在它周围添加类型化包装。我建议您也这样做。不,根据(在“隐式转换”下),这并不总是安全的INT
可以隐式转换为很多,但不能转换为DATE
,TIME
,DATETIMEOFFSET
,DATETIME2
,UNIQUEIDENTIFIER
,IMAGE
,NTEXT
,XML
,CLR-UDTs和HIERARCHYID
。其中一些类型比其他类型更常见,但在实践中很容易遇到与DATETIME2
和UNIQUEIDENTIFIER
不兼容的情况
没有任何T-SQL类型可以隐式转换为其他类型,因此如果您不知道目标类型,就没有好的方法可以这样做。在所有类型中,字符类型
CHAR
和VARCHAR
具有最隐式的转换,只缺少BINARY
、VARBINARY
(您的情况)和很少使用的TIMESTAMP
NCHAR
和NVARCHAR
紧随其后,另外缺少不推荐的IMAGE
类型。考虑到所有这一切,NVARCHAR
显然是默认值的最佳选择,因为它可以表示所有字符串,并且仅在隐式转换为二进制
类型时失败,这比INT
不转换为的类型更不常见(即使对于NULL
)。AddWithValue适用于DBNULL,当value==null时会出现问题。我建议您完全避免AddWithValue,并显式指定正确的参数类型。这不仅可以避免意外,在某些情况下还可以大大提高性能。在顶级代码中构造参数时,这一切都很好,但是对于在提取层中深层构造SqlParameters的框架或API来说没有太大意义。@MikhailLobanov:如果目标数据类型是varbinary,请参见链接问题。@DanGuzman:我同意,这就是我们下次完全重写数据访问层时要做的事情。我们已经吸取了教训。目前,我们有大量的遗留代码,这些代码没有提供正确类型的参数,也没有计划在短期内进行重写。哎哟,缺少的uniqueidentifier
转换会破坏很多代码。谢谢这不是我所希望的,但它完全回答了这个问题。显然,用文本NULL
替换参数是解决此问题的唯一方法。。。