【问题标题】:Is it possible to coalesce string and DBNull in C#?是否可以在 C# 中合并字符串和 DBNull?
【发布时间】:2010-12-05 11:12:14
【问题描述】:

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

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

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

CS0019:操作员 '??'不能应用于'string'和'System.DBNull'类型的操作数

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

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

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

【问题讨论】:

    标签: c# null-coalescing-operator


    【解决方案1】:

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

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

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

    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; 
    }
    

    还要注意,我明确声明了参数类型。

    【讨论】:

    • 关于信息,由 CommandBuilder 创建的命令有一组为您构建的参数。但是,同样的问题出现在您不能只期望将 null 传递给生成的 ParameterCollection 中的参数之一。您仍然必须按照上述说明进行操作。
    • 你也可以创建一个扩展方法来做到这一点。
    • @John 不,你不能,因为扩展方法仍然会返回不同类型的值。您可以封装 Execute__() 方法,但这并没有那么有用。
    • 我的意思是foreach中的位可能是SqlCommand的扩展方法。
    【解决方案2】:
    new SqlParameter("@theParam", (object)theParam ?? DBNull.Value)
    

    【讨论】:

    • 您甚至不需要将它们都强制转换为 null。我通常会做 new SqlParameter("@theParam", (object)theParam ?? DBNull.Value)
    【解决方案3】:

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

    【讨论】:

    • (进行了无关紧要的编辑,以便让我投票赞成这个答案。)
    【解决方案4】:

    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))
    

    【讨论】:

    • 是的,GetDataValue 方法是我过去处理这个问题的方式。我真的希望有一个内置的行为。就像您可以在 ADO.NET 类上指定的属性一样,当它获得空值时,它将替换 DBNULL.Value(或您指定的东西)。
    • 是的,我也希望能够指定 null 并让它自动映射到 DBNull。
    • @LoveMe:算我一个,因为这应该默认处理!
    【解决方案5】:

    您不能使用 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)
    );
    

    【讨论】:

      【解决方案6】:

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

      @theParam as varchar(50) = null
      

      然后在你的 csharp 中

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

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

      【讨论】:

      • 我认为您的意思是“!=”,修复了您的示例。
      • 好的,谢谢。我在尝试让代码块工作时注意到了这一点,却忘记了返回并进行编辑。
      【解决方案7】:

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

      【讨论】:

      • 是的,你错了,-1。尝试只传递 null 将导致 SQL Server 给出“参数...不存在”错误。
      • Erich,我会假设您在这一点上是正确的,因为我已经观察到您在 VB.NET 中多次描述的行为。除了这是 C# 之外,我不知道还有哪些不同的情况可以让它为我工作,而不是 erikkallen 或 Beska。
      • 啊,我通常使用SqlHelper类,我只是在里面找到了这一行: If (p.Direction = ParameterDirection.InputOutput OrElse p.Direction = ParameterDirection.Input) AndAlso p.Value Is Nothing Then p .Value = DBNull.Value End If ... 这就是它起作用的原因。
      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        使用这个语法:

        (theParam 作为对象) ?? (DBNull.Value 作为对象)

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

        【讨论】:

        • 1) 只需要投一边,都大材小用; 2) 使用as 除非您继续检查强制转换的返回值(即作为类型切换)是一个坏主意 - 正常的强制转换可以更清晰地传达意图。
        【解决方案10】:

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

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

        或者如果空白也可以

        (theParam == null) ? DBNull.Value : theParam
        

        【讨论】:

        • Nopeternary 也要求类型匹配。
        猜你喜欢
        • 2012-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-23
        • 2011-06-06
        • 2013-12-30
        • 1970-01-01
        相关资源
        最近更新 更多