为什么DBNull.Value需要正确的SqlDbType?

Cod*_*gry 4 c# sql-server

我编写了一堆通用的C#辅助函数作为我的SQL查询的基础.所有null对象参数都转换为a DBNull.Value.但是当目标列是a时varbinary,它告诉我该string类型与该类型不兼容,binary我必须转换.

为什么我需要SqlDbType一个DBNull.Value在C#中?有没有通用的方法来解决这个问题?

// works for nvarchar but throws for varbinary columns
// notice the lack of SqlDbType as I don't know it for a null object
var param1 = new SqlParameter();
param1.Name = "@Param1";
param1.Value = DBNull.Value;
Run Code Online (Sandbox Code Playgroud)

它是一个空的...它不需要任何类型.SQL服务器知道null的类型.在SQL你说[Column] IS NULL不是[Column] IS TYPE NULL.所以null是无类型的.并且目标列是从查询解析而不是SqlDbType.

我发现这非常不直观(显式输入空值).我想知道我是否遗漏了一些东西......

代码是这样的:

SqlCommand CreateCommand(string query, params object[] arguments) { ... }
// in here I walk the parameters and build the proper SqlParameter for them.
// this saves me from setting up the SqlParameters by hand.
Run Code Online (Sandbox Code Playgroud)

PS:String = NVarChar和Binary表示VarBinary.说真的...... PPS:对EF或类似的东西不感兴趣.

And*_*yev 6

如果不设置DBType你的SqlParameter明确-它有默认值SqlDBType.NVarchar(见MDSN仅供参考).

这就是你得到异常的原因 - 你的参数类型是NVarchar目标列VarBinary.

请注意,您必须DBType明确设置,因为显然ADO.NET无法从中推断出数据类型DBNull.Value.

null 不是一种类型 - 它只是缺乏价值,所以如果我给你null你不能推断它是否缺席,varchar或者,例如,没有int.

UPDATE

我做了一些实验.让我们运行简单的代码:

SqlCommand cmd = new SqlCommand("select * from sometable where somecolumn = @Param1");
cmd.Connection = _MyConnection;

var param = new SqlParameter();
param.ParameterName = "@Param1";
param.Value = DBNull.Value;
cmd.Parameters.Add(param);

cmd.Connection.Open();
try
{
    cmd.ExecuteNonQuery();
}
finally
{
    cmd.Connection.Close();
}
Run Code Online (Sandbox Code Playgroud)

正如我们从SQL Profiler中看到的,这将被转换为实际的SQL查询:

exec sp_executesql 
    N'select * from sometable where somecolumn = @Param1',
    N'@Param1 nvarchar(4000)',
    @Param1=NULL
Run Code Online (Sandbox Code Playgroud)

所以在这里,如果somecolumn数据类型无法隐式转换,varchar只是因为@Param1数据类型被显式设置为,我们肯定会得到异常varchar.

初看起来 - 这可以是足够好的解释,对吧?

但有一个问题令我感到困惑.如果我们不执行直接查询,而是执行具有varbinaryas参数的"虚拟"存储过程,那么让我们说:

create procedure [dbo].[usp_Test]
(
    @Param1 varbinary(max)
)
as
begin
    set nocount on

    select null
end
Run Code Online (Sandbox Code Playgroud)

现在让我们试着把它称为:

SqlCommand cmd = new SqlCommand("dbo.usp_Test");
cmd.CommandType = CommandType.StoredProcedure;
... and so on (the rest of code dealing with parameter)
Run Code Online (Sandbox Code Playgroud)

现在在SQL Profiler中我们将看到这样:

exec dbo.usp_Test @Param1=NULL
Run Code Online (Sandbox Code Playgroud)

这很奇怪,因为如果没有明确规范参数类型,这绝对是合法的.实际上,如果我们在SQL Management Studio中调用它 - 我们不会得到任何异常.

这部分ADO.NET行为对我来说很奇怪.可能SQL Profiler没有显示完整的过程(尽管事实上我打开了所有事件跟踪),我不知道.

反正-建议仍然是相同的-总是指定DBType你的SqlParameter明确.

  • @CodeAngry,SQL是一种强类型语言,因此所有参数都必须具有数据类型,而不管其值(包括DBNull.Value).在向数据库发送参数时,.NET必须遵循这些规则,即使它在幕后为您执行.在需要时,SQL Server还将根据数据类型优先级规则隐式地将参数转换为不同的数据类型. (2认同)