始终将 DBNull SqlParameter 键入为 int 是不是安全?
Posted
技术标签:
【中文标题】始终将 DBNull SqlParameter 键入为 int 是不是安全?【英文标题】:Is it safe to always type a DBNull SqlParameter as an int?始终将 DBNull SqlParameter 键入为 int 是否安全? 【发布时间】:2016-12-21 18:21:53 【问题描述】:最近,我们遇到了以下问题:
Null value in a parameter varbinary datatype简而言之:myCommand.Parameters.AddWithValue("@SomeParameter", DBNull.Value);
显然将 DBNull 参数“类型化”为 nvarchar,它可以隐式转换为几乎所有其他类型,但不幸的是,不是 varbinary
,从而产生以下错误:
不允许从数据类型 nvarchar 到 varbinary(max) 的隐式转换。使用 CONVERT 函数运行此查询。
不幸的是,链接问题中建议的解决方案不适用,因为我们在数据访问库的深处使用AddWithValue
,该库旨在从参数类型推断数据类型并且不支持添加“真实”SQL服务器类型。
我通过将 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 支持的任何其他类型。
忽略AddWithValue has some well-known shortcomings(我们非常清楚)这一事实,使用这种方法是否会出现任何问题? SqlParameterCollection.AddWithValue
的设计者是否有充分的理由不“默认”这样做,而我忽略了这一点?
【问题讨论】:
AddWithValue 与 DBNULL 配合使用效果很好,当 value == null 时会出现问题 我建议您完全避免 AddWithValue 并明确指定参数的正确类型。这不仅可以避免意外,还可以在某些情况下显着提高性能。 当您在***代码中构造参数时,这一切都很好,但对于在提取层深处构造 SqlParameters 的框架或 API 来说没有多大意义。跨度> @MikhailLobanov:当目标数据类型为 varbinary 时不会,请参阅链接问题。 @DanGuzman:我同意,这就是我们下次完全重写数据访问层时要做的事情。已经吸取了教训。目前,我们有大量的遗留代码不提供正确的参数类型,并且没有计划在短期内重写。 【参考方案1】:不,这并不总是安全的,根据conversion chart(在“隐式转换”下)。 INT
可以隐式转换为很多,但不能转换为 DATE
、TIME
、DATETIMEOFFSET
、DATETIME2
、UNIQUEIDENTIFIER
、IMAGE
、NTEXT
、TEXT
、@987654 CLR UDT 和 HIERARCHYID
。其中一些类型比其他类型更常见,但与DATETIME2
和UNIQUEIDENTIFIER
的不兼容在实践中很容易遇到。
没有任何 T-SQL 类型可以隐式转换为其他所有类型,因此如果您不知道目标类型,则确实没有好方法。在所有类型中,字符类型CHAR
和VARCHAR
具有最多的隐式转换,仅缺少BINARY
、VARBINARY
(您的情况)和很少使用的TIMESTAMP
。 NCHAR
和 NVARCHAR
紧随其后,另外缺少已弃用的 IMAGE
类型。考虑到这一切,NVARCHAR
显然是默认值的最佳选择,因为它可以表示所有字符串,并且仅在隐式转换为 BINARY
类型时失败,这比 INT
不转换的类型不太常见到(即使是NULL
)。
【讨论】:
哎呀,缺少的uniqueidentifier
转换会破坏很多代码。谢谢!这不是我所希望的,但它完全回答了这个问题。显然,用文字 NULL
替换参数是解决这个问题的唯一方法......【参考方案2】:
AddWithValue
有更多问题。存在query plan cache pollution 的问题是因为它创建了NVARCHAR(X)
类型的参数,其中X
是值的确切长度。由于NVARCHAR(73)
是与NVARCHAR(74)
不同的类型,SQL Server 查询计划缓存被许多仅在参数类型上不同的相同计划污染。另一个问题是当VARCHAR
类型的列参与比较时,NVARCHAR
类型的参数会导致类型转换,而不是取消在所述列上使用索引的资格。
反对AddWithValue
的原因有很多,我从不使用它,从不推荐它,并且总是在它周围添加 typed 包装器。我建议你也这样做。
【讨论】:
以上是关于始终将 DBNull SqlParameter 键入为 int 是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章
System.DBNull.System.IConvertible.ToDateTime