如何使用 SqlDataRecord 处理 Nullable 类型

Posted

技术标签:

【中文标题】如何使用 SqlDataRecord 处理 Nullable 类型【英文标题】:How do you handle Nullable type with SqlDataRecord 【发布时间】: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);

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

【问题讨论】:

【参考方案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 方法。

【讨论】:

以上是关于如何使用 SqlDataRecord 处理 Nullable 类型的主要内容,如果未能解决你的问题,请参考以下文章

如何从PowerShell /批处理文件中的网络路径运行命令

>在批处理命令之前或之后进行nul重定向[重复]

批处理遇到问题解析

win批处理

使用IO流将数据库中数据生成一个文件,结果使用Notepad++打开部分数据结尾出现NUL

BAT批处理,如何判断变量中,是不是包含了指定的字符串?