C# 如何使用DBNull.Value清晰快速地参数化空字符串

C# 如何使用DBNull.Value清晰快速地参数化空字符串,c#,.net,sql-server,naming-conventions,C#,.net,Sql Server,Naming Conventions,我厌倦了编写以下代码: /* Commenting out irrelevant parts public string MiddleName; public void Save(){ SqlCommand = new SqlCommand(); // blah blah...boring INSERT statement with params etc go here. */ if(MiddleName==null){ myCmd.Parameters.

我厌倦了编写以下代码:

/* Commenting out irrelevant parts
public string MiddleName;
public void Save(){
    SqlCommand = new SqlCommand();
    // blah blah...boring INSERT statement with params etc go here. */
    if(MiddleName==null){
        myCmd.Parameters.Add("@MiddleName", DBNull.Value);
    }
    else{
        myCmd.Parameters.Add("@MiddleName", MiddleName);
    }
    /*
    // more boring code to save to DB.
}*/
所以,我写了这个:

public static object DBNullValueorStringIfNotNull(string value)
{
    object o;
    if (value == null)
    {
        o = DBNull.Value;
    }
    else
    {
        o = value;
    }
    return o;
}

// which would be called like:
myCmd.Parameters.Add("@MiddleName", DBNullValueorStringIfNotNull(MiddleName));
如果这是一个很好的方法,那么您建议使用什么方法名称?DBNullValuerStringNotNull有点冗长和混乱。

我也对完全缓解这个问题持开放态度。我很乐意这样做:

myCmd.Parameters.Add("@MiddleName", MiddleName==null ? DBNull.Value : MiddleName);
但这不起作用,因为“运算符”不能应用于“string”和“System.DBNull”类型的操作数


如果重要的话,我可以使用C#3.5和SQL Server 2005。

将您的任何一个值转换为
对象,它将编译

myCmd.Parameters.Add("@MiddleName", MiddleName==null ? (object)DBNull.Value : MiddleName);

是的,我们都喜欢做
myCmd.Parameters.Add(“@MiddleName”,MiddleName??DBNull.Value”)。或者更好的是,让奇怪的SqlClient层理解,在添加参数时,CLR
null
应该映射到
DBNull.Value
。不幸的是.Net类型的系统关闭了第一个备选方案,而SqlClient的实现关闭了第二个备选方案


我会使用一个众所周知的函数名,比如or。任何数据库开发人员都会在瞬间识别出他们所做的事情,仅从名称就可以了。

就我个人而言,这就是我使用扩展方法所做的事情(确保它进入一个静态类)

那你就有了

myCmd.Parameters.Add("@MiddleName", MiddleName.GetStringOrDBNull());

我宁愿给你两个完全不同的建议:

  • 使用ORM。有很多非侵入式ORM工具

  • 为构建命令编写自己的包装器,具有更清晰的界面。比如:

    public class MyCommandRunner {
      private SqlCommand cmd;
    
      public MyCommandRunner(string commandText) {
        cmd = new SqlCommand(commandText);
      }
    
      public void AddParameter(string name, string value) {
        if (value == null)
         cmd.Parameters.Add(name, DBNull.Value);
        else
          cmd.Parameters.Add(name, value);
      }
    
      // ... more AddParameter overloads
    }
    
  • 如果将
    AddParameter
    方法重命名为just
    Add
    ,则可以非常灵活地使用它:

    var cmd = new MyCommand("INSERT ...")
      {
        { "@Param1", null },
        { "@Param2", p2 }
      };
    

    我建议使用可为null的属性,而不是公共字段和“AddParameter”方法(不知道这段代码是优化的还是正确的,我不知道):


    @大卫:谢谢你的建议。下面的方法非常有效

    MiddleName ?? (object)DBNull.Value
    

    使用
    SqlString.Null
    而不是
    DBNull.Value
    ,可以避免显式转换为
    对象

    MiddleName ?? SqlString.Null
    
    int、datetime等有相应的类型。下面是一段代码片段,还有几个示例:

     cmd.Parameters.AddWithValue("@StartDate", StartDate ?? SqlDateTime.Null);
     cmd.Parameters.AddWithValue("@EndDate", EndDate ?? SqlDateTime.Null);
     cmd.Parameters.AddWithValue("@Month", Month ?? SqlInt16.Null);
     cmd.Parameters.AddWithValue("@FormatID", FormatID ?? SqlInt32.Null);
     cmd.Parameters.AddWithValue("@Email", Email ?? SqlString.Null);
     cmd.Parameters.AddWithValue("@ZIP", ZIP ?? SqlBoolean.Null);
    


    我根本不会编写null实例-如果值为null,请从insert语句中省略。但这不起作用。-请具体说明,为什么人们应该猜测?@Andrey:他的语句不会编译(编译器会说
    DBNull
    string
    之间没有隐式转换)。@OMG:理论上很好,但很少有人会根据特定参数是否为null编写不同的insert语句。它增加了大量维护开销,但实际效益很小或根本没有(当然,除了能够利用列默认值之外)。@OMG,它能快多少(说真的,我不熟悉.net和SQL Server的通信方式)?有了TCP,我知道对于如此小的数据包,大部分时间都会花在RTT上。有很好的理由说明
    null
    没有映射到
    DBNull.Value
    。也就是说,它强制您为每个参数指定一个值,即使该“值”是数据库空值??对于这个特定的环境,值可能很好,你必须从编译器的角度考虑这个问题:那个表达式的返回类型是什么?编译器是否应该寻找最常见的祖先,即使它是
    对象
    ?也许我不清楚“CLR类型系统”是什么意思:无法建立表达式
    (type1??type2)
    的编译时类型。我们说的是同一件事。我喜欢你还有一匹小马作为你的化身。@David:是的,我抓住它,让它保持不变:)甜蜜:
    MiddleName??(object)DBNull.Value
    有效!或者更好的是
    公共静态只读对象DBNullValue=(object)DBNull.Value带有
    MiddleName??DBNullValue
    !你是我的英雄。啊,我还得再等3分钟才能接受你的回答。@David:太好了!甚至没有考虑过合并。我必须在我的代码中开始这样做。我考虑按您描述的那样缓存该值,但希望将代码保留在一行内。@davidmudoch:而不是
    MiddleName==null?(object)DBNull.Value:MiddleName
    ,也可以使用
    MiddleName??SqlString.Null()。这在某些数据库上可能不起作用。我确实喜欢这个名称。所以这是+1。不过,我更喜欢接受答案的解决方案。:-)看看你是不是改变了主意,用100个if语句代替了100个三元运算符,我实际上用的是空合并运算符。您必须同意
    MiddleName??DBNullValue
    非常简单。但是+1也用于您的注释。+1但是我更喜欢只对实际的
    null
    值使用
    DBNull.Value
    ,并保留空字符串。我使用
    return(object)obj??DBNull.Value在我的扩展方法版本中。我有很多“这样”的参数,在这种情况下,扩展方法就是最好的选择。谢谢value.HasValue不编译:HasValue用于可为null的对象类型,而不是对象类型
    
    MiddleName ?? SqlString.Null
    
     cmd.Parameters.AddWithValue("@StartDate", StartDate ?? SqlDateTime.Null);
     cmd.Parameters.AddWithValue("@EndDate", EndDate ?? SqlDateTime.Null);
     cmd.Parameters.AddWithValue("@Month", Month ?? SqlInt16.Null);
     cmd.Parameters.AddWithValue("@FormatID", FormatID ?? SqlInt32.Null);
     cmd.Parameters.AddWithValue("@Email", Email ?? SqlString.Null);
     cmd.Parameters.AddWithValue("@ZIP", ZIP ?? SqlBoolean.Null);
    
    myCmd.Parameters.Add("@MiddleName", MiddleName ?? (object)DBNull.Value);