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
替换参数是解决此问题的唯一方法。。。