C# 如何将可为Null的int转换为SQL Null值?

C# 如何将可为Null的int转换为SQL Null值?,c#,sql-server,linq-to-sql,C#,Sql Server,Linq To Sql,我需要使用DataContext的ExecuteCommand方法对具有多个基础表的视图执行更新。我之所以使用这种方法,是因为在对具有多个基础表的视图执行这种类型的操作时,linqToSQL的已知限制 我现有的SQL语句类似于下面的语句,我将newFieldID设置为空值,只是为了说明这个问题。在应用程序中,newFieldID被分配一个传递的参数,实际上可以是一个整数值;但我的问题是特定于提供的值为空类型的情况: using (var _ctx = new MyDataContext()) {

我需要使用DataContext的ExecuteCommand方法对具有多个基础表的视图执行更新。我之所以使用这种方法,是因为在对具有多个基础表的视图执行这种类型的操作时,linqToSQL的已知限制

我现有的SQL语句类似于下面的语句,我将newFieldID设置为空值,只是为了说明这个问题。在应用程序中,newFieldID被分配一个传递的参数,实际上可以是一个整数值;但我的问题是特定于提供的值为空类型的情况:

using (var _ctx = new MyDataContext())
{
   int? newFieldID = null;
   var updateCmd = "UPDATE [SomeTable] SET fieldID = " + newFieldID + " 
   WHERE keyID = " + someKeyID;

   try
   {
      _ctx.ExecuteCommand(updateCmd);
      _ctx.SubmitChanges();
   }
   catch (Exception exc)
   {
      // Handle the error...
   }
}
此代码将失败,原因很明显,在newFieldID为null值的情况下,updateCmd将无法完成。那么,如何用SQL null替换或转换CLR null值来完成该语句呢

我知道我可以将所有这些转移到存储过程中,但我正在寻找现有场景的答案。我尝试过使用DBNull.Value进行实验,但除了用它替换要在语句中使用的newFieldID之外,简单地将它放在字符串中会破坏语句的有效性

var updateCmd = "UPDATE [SomeTable] SET fieldID =" + (newFieldID == null ? "null" : Convert.ToString(newFieldID)) + " WHERE keyID = " + someKeyID;
另外,将其括在单引号内:

var updateCmd = "UPDATE [SomeTable] SET fieldID = '" + DBNull.Value + "'
   WHERE keyID = " + someKeyID;
将完成语句,但字段的值将转换为整数0,而不是SQL null


那么,在这种情况下,如何将CLR null或可为null的int转换为SQL null值呢?

通常,在使用存储过程或准备好的语句时,使用参数来赋值。当您有
DbParameter
时,可以将
null
DBNull.Value
分配给Value属性或参数

如果您想将
null
作为语句中的文本,只需使用SQL关键字
null

var updateCmd = "UPDATE [SomeTable] SET fieldID = NULL WHERE keyID = " + someKeyID;
正确方法:使用
ExecuteCommand
不仅接受命令文本,还接受参数数组,并使用参数化查询而不是命令字符串连接:

var updateCmd = "UPDATE [SomeTable] SET fieldID = {0} WHERE keyID = {1}";
_ctx.ExecuteCommand(updateCmd, new [] {newFieldID, someKeyID});
它不仅可以阻止sql注入,还可以为您执行以下操作(来自MSDN说明):

如果其中任何一个参数为null,则将其转换为DBNull.Value


尝试检查newFieldID==null并相应地更改语句。 类似于下面的内容或使用单独的if/else语句

var updateCmd = "UPDATE [SomeTable] SET fieldID =" + (newFieldID == null ? "null" : Convert.ToString(newFieldID)) + " WHERE keyID = " + someKeyID;

正如Andy Korneyev和其他人所指出的,参数化数组方法是最好的,并且在使用准备好的语句时可能是更合适的方法。由于我使用的是LinqToSQL,因此建议使用带有第二个参数的ExecuteCommand方法,该参数包含一个参数数组,但其用法有以下注意事项

  • 查询参数的类型不能为System.Nullable`1[System.Int32][](在本例中我试图解决的主要问题)
  • 所有参数必须为同一类型
  • Shankar的答案是有效的,尽管它可能很快变得非常冗长,因为参数的数量可能会增加

    因此,我对这个问题的解决方法是在某种程度上混合使用Andy和Shankar建议的参数,通过创建一个helper方法来处理空值,该空值将使用带有参数映射的SQL语句和实际参数

    我的助手方法是:

    private static string PrepareExecuteCommand (string sqlParameterizedCommand, object [] parameterArray) 
        {
            int arrayIndex = 0;
            foreach (var obj in parameterArray)
            {
                if (obj == null || Convert.IsDBNull(obj)) 
                {
                    sqlParameterizedCommand = sqlParameterizedCommand.Replace("{" + arrayIndex + "}", "NULL"); 
                }
                else
                    sqlParameterizedCommand = sqlParameterizedCommand.Replace("{" + arrayIndex + "}", obj.ToString());
                ++arrayIndex;
            }
            return sqlParameterizedCommand;
        }
    
    因此,我现在可以使用具有潜在空值的参数执行语句,如下所示:

    int? newFieldID = null;
    int someKeyID = 12;
    var paramCmd = "UPDATE [SomeTable] SET fieldID = {0} WHERE keyID = {1}";
    var newCmd = PrepareExecuteCommand(paramCmd, new object[] { newFieldID });
    
    _ctx.ExecuteCommand(newCmd);
    _ctx.SubmitChanges();
    
    这通过参数数组减轻了先前引用的ExecuteCommand的两个限制。空值会得到适当的转换,对象数组可能会有不同的.NET类型


    我将Shankar的建议作为答案,因为它激发了这个想法,但发布我的解决方案是因为我认为它增加了一点灵活性。

    请不要使用字符串连接。创建参数化查询并按原样传递参数。问题不在于如何转换可为null的整数。首先,应该使用参数化查询。在这里,当newFieldID为null时,您可以简单地编写“fieldID=null”。我喜欢这个解决方案,但是如果参数值不同,您将如何实现它?换句话说,这里的情况是一个整数,但假设参数列表推断出一个隐式类型的数组,该数组可能包括整数、字符串和布尔值?@Mark,因为第二个方法参数的类型是
    object[]
    -在这种情况下只需使用显式类型的对象数组:
    newobject[]{integerParam,stringParam,booleanParam}
    。试图使用“查询参数不能是'System.object'类型”中的对象数组错误。你知道为什么吗?这是.NET 4.5。尽管如此,我还是会接受你的答案,我喜欢。@Mark oops…在寻找你所描述的行为的可能原因时,我已经将我提到的MSDN文章中的框架版本从当前版本切换到了。该文章版本下面的评论证明了该方法实际上是错误的,并且Microsoft没有尝试修复此问题。因此,我的回答似乎对您完全没有用处,抱歉……相反,您的初始回答针对了初始问题的上下文,因此您完全正确。我传递不同参数类型的问题在某种程度上是初始问题的一个分支。谢谢!