【问题标题】:How do you handle Nullable type with SqlDataRecord如何使用 SqlDataRecord 处理 Nullable 类型
【发布时间】:2012-01-14 08:59:00
【问题描述】:

我正在解析 XML (LINQ to XML),并且在元素/属性为空的情况下使用可空类型(int?decimal?)。但是,在构建我的集合以传递给数据库(使用 TVP)时,我不知道如何处理值实际上为空的情况。我不能将 null 传递给 SqlDataRecord SetInt32 或 SetDecimal,我不想设置为零....我实际上希望它为 null。

告诉我int 没有过载?

下面的Count是一个可以为空的类型(int?Count)

SqlDataRecord rec = new SqlDataRecord(
              new SqlMetaData("Name", SqlDbType.VarChar, 50),
              new SqlMetaData("Type", SqlDbType.VarChar, 50),
              new SqlMetaData("Count", SqlDbType.Int));

   rec.SetString(0, dm.Name);
   rec.SetString(1, dm.Type);
   rec.SetString(2, dm.Count);

任何想法如何在不传递零(保持空值)的情况下处理这个问题?

【问题讨论】:

    标签: c# sql-server-2008 sqldatatypes


    【解决方案1】:

    正如@phoog 推荐的,不同类型的扩展方法:

    public static class ExtensionSqlDataRecord
    {
        public static void SetDateTime(this SqlDataRecord record, int ordinal, DateTime? value)
        {
            if (value != null)
            {
                record.SetDateTime(ordinal, (DateTime)value);
            }
            else
            {
                record.SetDBNull(ordinal);
            }
        }
    
        public static void SetInt32(this SqlDataRecord record, int ordinal, int? value)
        {
            if (value != null)
            {
                record.SetInt32(ordinal, (int)value);
            }
            else
            {
                record.SetDBNull(ordinal);
            }
        }
    
        public static void SetByte(this SqlDataRecord record, int ordinal, byte? value)
        {
            if (value != null)
            {
                record.SetByte(ordinal, (byte)value);
            }
            else
            {
                record.SetDBNull(ordinal);
            }
        }
    
        public static void SetDecimal(this SqlDataRecord record, int ordinal, decimal? value)
        {
            if (value != null)
            {
                record.SetDecimal(ordinal, (decimal)value);
            }
            else
            {
                record.SetDBNull(ordinal);
            }
        }
    
        public static void SetBoolean(this SqlDataRecord record, int ordinal, bool? value)
        {
            if (value != null)
            {
                record.SetBoolean(ordinal, (bool)value);
            }
            else
            {
                record.SetDBNull(ordinal);
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      也许你可以试试下面的。

      if(dm.Count == null){
        rec.SetDBNull(2)
      }
      else{
       rec.SetString(2, dm.Count);
      }
      

      【讨论】:

        【解决方案3】:

        扩展方法:

        static class SqlDataRecordExtensions
        {
            static void SetNullableInt32(this SqlDataRecord rec, int index, Int32? value)
            {
                if (value.HasValue)
                    rec.SetInt32(index, value.GetValueOrDefault());
                else
                    rec.SetDBNull(index);
            }
        }
        

        或者,按照 D Stanley 的建议使用 SetSqlInt32

        static class SqlDataRecordExtensions
        {
            static void SetNullableInt32(this SqlDataRecord rec, int index, Int32? value)
            {
                rec.SetSqlInt32(index, value.HasValue ? value.GetValueOrDefault() : SqlInt32.Null);
                //                                      ^^^^^^^^^^^^^^^^^^^^^^^^^
                //                                      You can leave out the cast to (SqlInt32),
                //                                      because the conversion is implicit
            }
        }
        

        注意,2013 年 12 月 9 日:由于评论而返回此答案,我注意到一个小的改进机会,基于 Eric Lippert 的可空微优化系列,可以在 http://ericlippert.com/2012/12/20/nullable-micro-optimizations-part-one/ 找到。

        简而言之,虽然Value 属性需要更少的输入,因此对于程序员来说可能是更优化的,但如果 HasValue 为 false,它必须抛出异常。另一方面,GetValueOrDefault() 方法是一个简单的字段访问。正因为如此,GetValueOrDefault() 需要的指令更少,并且更有可能被内联,因此它更适合编译器和处理器。

        【讨论】:

        • SetValue(ordinal,object) 处理可空值和空值,但在类型和值长度检查方面有一点开销。性能差异可能不明显。
        • @RichardCollette 感谢您的评论;我没有想到。我怀疑您是对的,对于大多数应用程序而言,性能差异可能微不足道。不过,我确实倾向于基于哲学理由更喜欢更强的打字。您的评论也启发了我添加一个两年前我没有想到的小优化。
        • 不确定框架是否发生了变化,但它在版本 4 中是 SetDBNull(注意“B”大小写)。
        • @PhilCooper 什么 版本 4 中的 SetDBNull 是什么?什么“B”案例?
        • @phoog SetDbNull 是 SetDBNull 参见此处:SqlDataRecord.SetDBNull
        【解决方案4】:

        //我发现这在检查或处理可空类型时最适合我 //在我的示例中,我正在检查存储在 String[] 数组中的值

        sqlcmdUpdateRecord.Parameters.AddWithValue("@strSomeValue", (strRecord[(int) 
        DataFields.SomeDataField] as object) ?? DBNull.Value);
        

        【讨论】:

          【解决方案5】:

          应该使用SetSqlInt32 而不是SetString。试试

          rec.SetSqlInt32(2, (dm.Count.HasValue ? new SqlInt32(dm.Count) : SqlInt32.Null) );
          

          【讨论】:

          • 那必须是rec.SetSqlInt32(2, (dm.Count.HasValue ? new SqlInt32(dm.Count.Value) : SqlInt32.Null) );
          • 此外,正如我在编辑答案时意识到的那样,从 int 到 SqlInt32 的隐式转换意味着您可以这样做:rec.SetSqlInt32(2, dm.Count.HasValue ? dm.Count.Value : SqlInt32.Null);
          【解决方案6】:

          我从未使用过SqlDataRecord,但在使用DataTableDataRow 时,或者在使用参数化查询时,我使用DBNull.Value 指定null

          使用SqlDataRecord,看起来您可以使用SetDBNull 方法。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-11-11
            • 2023-03-15
            • 1970-01-01
            • 2023-03-07
            • 2017-09-11
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多