10

我正在编写一个 C# 例程来调用存储过程。在我传入的参数列表中,其中一个值可能合法地为空。所以我想我会使用这样的一行:

cmd.Parameters.Add(new SqlParameter("@theParam", theParam ?? DBNull.Value));

不幸的是,这会返回以下错误:

CS0019:操作员 '??' 不能应用于“字符串”和“System.DBNull”类型的操作数

现在,这似乎很清楚,但我不明白其背后的原理。为什么这行不通?(通常,当我不明白为什么某事不工作时,并不是它不能工作……而是我做错了。)

我真的必须将其扩展为更长的 if-then 语句吗?

编辑:(顺便说一句,对于那些建议只使用“null”的人来说,它不起作用。我最初认为 null 也会自动翻译成 DBNull,但显然不会。(谁知道?))

4

10 回答 10

16

不是那样的,不。类型必须匹配。三元组也是如此。

现在,通过“匹配”,我并不是说它们必须相同。但它们必须是分配兼容的。基本上:在同一个继承树中。

解决此问题的一种方法是将字符串转换为对象:

var result = (object)stringVar ?? DBNull.Value;

但我不喜欢这样,因为这意味着您更多地依赖 SqlParameter 构造函数来获取正确的类型。相反,我喜欢这样做:

cmd.Parameters.Add("@theParam", SqlDbTypes.VarChar, 50).Value = theParam;
// ... assign other parameters as well, don't worry about nulls yet

// all parameters assigned: check for any nulls
foreach (var p in cmd.Parameters) 
{ 
    if (p.Value == null) p.Value = DBNull.Value; 
}

另请注意,我明确声明了参数类型。

于 2009-10-09T19:54:35.427 回答
5
new SqlParameter("@theParam", (object)theParam ?? DBNull.Value)
于 2009-10-09T19:49:43.970 回答
3

这 ??运算符如果不为空则返回左操作数,否则返回右操作数。但在你的情况下,它们是不同的类型,所以它不起作用。

于 2009-10-09T19:50:10.520 回答
2

您不能使用 null coalesce 运算符的原因是它必须返回一种类型并且您提供的类型不止一种。theParam是一个字符串。DbNull.Value是对System.DbNull类型的静态实例的引用。这就是它的实现的样子;

public static readonly DBNull Value = new DBNull(); 
//the instantiation is actually in the 
//static constructor but that isn't important for this example

所以如果你有一个 NullCoalesce 方法,它的返回类型是什么?它不能同时是 System.String 和 System.DbNull,它必须是其中之一,或者是一个共同的父类型。

这导致了这种类型的代码;

cmd.Parameters.Add(
    new SqlParameter("@theParam", (object)theParam ?? (object)DBNull.Value)
);
于 2009-10-09T19:51:38.010 回答
2

Null Coalesce 运算符仅适用于相同类型的数据。您不能将 NULL 发送到 SqlParamater,因为这会使 Sql Server 说您没有指定参数。

您可以使用

new SqlParameter("@theParam", (object)theParam ?? (object)DBNull.Value)

或者您可以创建一个在找到 null 时返回 DBNull 的函数,例如

public static object GetDataValue(object o)
{
    if (o == null || String.Empty.Equals(o))
        return DBNull.Value;
    else
        return o;
}

然后打电话

new SqlParameter("@theParam", GetDataValue(theParam))
于 2009-10-09T19:56:33.393 回答
1

在您的存储过程中,当您声明传入变量时,将其设置为 var 等于 null 然后不要从您的 csharp 代码中传递它,然后它将从 sql 中获取默认值

@theParam as varchar(50) = null

然后在你的 csharp

if (theParam != null)
    cmd.Parameters.Add(new SqlParameter("@theParam", theParam));

这就是我通常将选项和/或默认值传递给存储过程的方式

于 2009-10-09T20:16:01.310 回答
0

我很确定将 null 传递给 SqlParameter 构造函数会导致它作为 DBNull.Value 发送...我可能弄错了,因为我使用 EnterpriseLibraries 进行数据库访问,但我很确定发送null 在那里很好。

于 2009-10-09T19:49:20.870 回答
0

cmd.Parameters.Add(new SqlParameter("@theParam", (theParam == null) ? DBNull.Value : theParam));

于 2009-10-09T19:51:16.687 回答
0

使用此语法:

(将参数作为对象) ?? (DBNull.Value 作为对象)

在这种情况下,运算符的两个部分 ?? 属于同一类型。

于 2009-10-09T20:07:22.343 回答
-1

不确定您的问题的具体答案,但是这个怎么样?

string.IsNullOrEmpty(theParam) ? DBNull.Value : theParam

或者如果空白是可以的

(theParam == null) ? DBNull.Value : theParam
于 2009-10-09T19:49:20.960 回答