始终将 DBNull SqlParameter 键入为 int 是否安全?
Is it safe to always type a DBNull SqlParameter as an int?
最近,我们 运行 解决了以下问题:
- Null value in a parameter varbinary datatype
简而言之:myCommand.Parameters.AddWithValue("@SomeParameter", DBNull.Value);
显然 "types" DBNull 参数作为 nvarchar,可以隐式转换为 几乎 所有其他类型,但是,不幸的是,不是 varbinary
,会产生以下错误:
Implicit conversion from data type nvarchar to varbinary(max) is not allowed. Use the CONVERT function to run this query.
不幸的是,链接问题中建议的解决方案不适用,因为我们在数据访问库的深处使用 AddWithValue
,该库旨在从参数类型推断数据类型并且不支持添加"real" SQL 服务器类型。
我 "fixed" 通过将 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 服务器支持的任何其他类型。
忽略 AddWithValue has some well-known shortcomings 这一事实(我们非常清楚这一点),使用这种方法是否会出现任何问题? SqlParameterCollection.AddWithValue
的设计者是否有充分的理由不这样做 "by default" 我忽略了?
AddWithValue
有更多问题。 query plan cache pollution 的问题是由于它创建了一个 NVARCHAR(X)
类型的参数,其中 X
是值的确切长度。由于 NVARCHAR(73)
与 NVARCHAR(74)
的 type 不同,SQL 服务器查询计划缓存被许多仅参数类型不同的相同计划污染.另一个问题是当类型 VARCHAR
的列参与比较并且类型 NVARCHAR
的参数导致类型转换而不是取消在所述列上使用索引的资格时。
反对 AddWithValue
的理由太多,我从不使用它,从不推荐它,并且总是在它周围添加 typed 包装器。我建议你也这样做。
不,根据 conversion chart(在 "implicit conversions" 下),这并不总是安全的。 INT
可以隐式转换为很多,但不能隐式转换为DATE
、TIME
、DATETIMEOFFSET
、DATETIME2
、UNIQUEIDENTIFIER
、IMAGE
、NTEXT
、TEXT
、XML
、CLR UDT 和 HIERARCHYID
。其中一些类型比其他类型更常见,但与 DATETIME2
和 UNIQUEIDENTIFIER
的不兼容在实践中很容易遇到。
没有 T-SQL 类型可以隐式转换为所有其他类型,因此如果您不知道目标类型,确实没有好的方法可以做到这一点。在所有类型中,字符类型 CHAR
和 VARCHAR
具有最多的隐式转换,仅缺少 BINARY
、VARBINARY
(您的情况)和较少使用的 TIMESTAMP
. NCHAR
和 NVARCHAR
紧随其后,此外还缺少已弃用的 IMAGE
类型。考虑到所有这些,NVARCHAR
显然是默认值的最佳选择,因为它可以表示所有字符串并且仅在隐式转换为 BINARY
类型时失败,而 BINARY
类型不如 INT
不会转换为(即使是 NULL
)。
最近,我们 运行 解决了以下问题:
- Null value in a parameter varbinary datatype
简而言之:myCommand.Parameters.AddWithValue("@SomeParameter", DBNull.Value);
显然 "types" DBNull 参数作为 nvarchar,可以隐式转换为 几乎 所有其他类型,但是,不幸的是,不是 varbinary
,会产生以下错误:
Implicit conversion from data type nvarchar to varbinary(max) is not allowed. Use the CONVERT function to run this query.
不幸的是,链接问题中建议的解决方案不适用,因为我们在数据访问库的深处使用 AddWithValue
,该库旨在从参数类型推断数据类型并且不支持添加"real" SQL 服务器类型。
我 "fixed" 通过将 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 服务器支持的任何其他类型。
忽略 AddWithValue has some well-known shortcomings 这一事实(我们非常清楚这一点),使用这种方法是否会出现任何问题? SqlParameterCollection.AddWithValue
的设计者是否有充分的理由不这样做 "by default" 我忽略了?
AddWithValue
有更多问题。 query plan cache pollution 的问题是由于它创建了一个 NVARCHAR(X)
类型的参数,其中 X
是值的确切长度。由于 NVARCHAR(73)
与 NVARCHAR(74)
的 type 不同,SQL 服务器查询计划缓存被许多仅参数类型不同的相同计划污染.另一个问题是当类型 VARCHAR
的列参与比较并且类型 NVARCHAR
的参数导致类型转换而不是取消在所述列上使用索引的资格时。
反对 AddWithValue
的理由太多,我从不使用它,从不推荐它,并且总是在它周围添加 typed 包装器。我建议你也这样做。
不,根据 conversion chart(在 "implicit conversions" 下),这并不总是安全的。 INT
可以隐式转换为很多,但不能隐式转换为DATE
、TIME
、DATETIMEOFFSET
、DATETIME2
、UNIQUEIDENTIFIER
、IMAGE
、NTEXT
、TEXT
、XML
、CLR UDT 和 HIERARCHYID
。其中一些类型比其他类型更常见,但与 DATETIME2
和 UNIQUEIDENTIFIER
的不兼容在实践中很容易遇到。
没有 T-SQL 类型可以隐式转换为所有其他类型,因此如果您不知道目标类型,确实没有好的方法可以做到这一点。在所有类型中,字符类型 CHAR
和 VARCHAR
具有最多的隐式转换,仅缺少 BINARY
、VARBINARY
(您的情况)和较少使用的 TIMESTAMP
. NCHAR
和 NVARCHAR
紧随其后,此外还缺少已弃用的 IMAGE
类型。考虑到所有这些,NVARCHAR
显然是默认值的最佳选择,因为它可以表示所有字符串并且仅在隐式转换为 BINARY
类型时失败,而 BINARY
类型不如 INT
不会转换为(即使是 NULL
)。