SqlDataAdapter.Fill() 溢出异常

Posted

技术标签:

【中文标题】SqlDataAdapter.Fill() 溢出异常【英文标题】:SqlDataAdapter.Fill() OverflowException 【发布时间】:2014-04-25 11:08:48 【问题描述】:

我需要从数据库中获取数据并执行以下代码:

using (var dataRetrievingCommand = new SqlCommand(selectQuery, connection))

    var dataTable = new DataTable("DataTable");
    var sda = new SqlDataAdapter(dataRetrievingCommand);
    sda.Fill(dataTable); //OverflowException here
    return dataTable;

问题是数据库中的某些列的类型为decimal(38,10)decimal(38, 0) 等。它们的值范围大于decimal 在C# 中的类型,这就是我得到OverflowException 的原因。

有没有什么方法可以在不修改 SQL 查询和数据库结构的情况下在 C# 中选择该数据?

如果 DB 值精度高于 C# 的值,则可以对值进行四舍五入。如果发生溢出,我希望看到 NULL,或者任何其他无法读取该值的标记。

调查详情:

这个问题听起来很流行,但我在微软论坛上发现只有one thread 有同样的问题,但那里没有答案。

SQL Server Management Studio 2008 本身无法在编辑数据模式下显示此类值 - 它显示“”并且不允许编辑这些单元格。 但是 SELECT、INSERT 和 UPDATE 命令可以正常工作并显示所有数据。

精度为 28 或更低的小数列可以正常工作,但任何精度更高的列都会失败,即使值不会导致溢出(如 decimal(38, 38) 可以像在插入时修剪 nvarchar 一样被修剪)。

sda.ContinueUpdateOnError = true;

不会改变任何东西。我认为它只是不是一个更新操作,所以这个属性被忽略了。

sda.FillError += (sender, args) =>

    args.Continue = true;
;

停止抛出异常,但根本不返回任何数据 - 只是一个无意义的行

 CaseName | Decimal_10_2 | Decimal_18_0 | Decimal_18_18 | Decimal_28_0 | Decimal_28_28 | Decimal_38_0 | Decimal_38_20 | Decimal_38_38 | Float | Int
----------+--------------+--------------+---------------+--------------+---------------+--------------+---------------+---------------+-------+----
 Null     |              |              |               |              |               |              |               |               |       |    

【问题讨论】:

【参考方案1】:

看来我自己找到了解决办法。

sda.ReturnProviderSpecificTypes = true;

之后,DataTable 单元格包含 System.Data.SqlTypes.SqlDecimalSystem.Data.SqlTypes.SqlString 等类型的值,可以通过自定义错误处理进一步转换为本机 .NET 数据类型。

然后我检查 SqlDecimal 值的PrecisionScale 属性,如果需要,使用SqlDecimal.AdjustScale() 方法对值进行四舍五入。可以通过Value 属性访问结果。查看我的实现:

public struct DecimalEx

    private decimal m_Value;

    public decimal Value
    
        get  return m_Value; 
    

    private bool m_IsNull;

    public bool IsNull
    
        get  return m_IsNull; 
    

    private bool m_IsOverflow;

    public bool IsOverflow
    
        get  return m_IsOverflow; 
    

    private bool m_IsRounded;

    public bool IsRounded
    
        get  return m_IsRounded; 
    

    private bool m_IsPositive;

    public bool IsPositive
    
        get  return m_IsPositive; 
    

    public DecimalEx(decimal value)
    
        m_Value = value;
        m_IsNull = false;
        m_IsOverflow = false;
        m_IsRounded = false;
        m_IsPositive = value >= 0;
    

    public static explicit operator DecimalEx(SqlDecimal dbValue)
    
        var result = new DecimalEx();
        if (dbValue.IsNull)
        
            result.m_Value = 0;
            result.m_IsNull = true;
            result.m_IsOverflow = false;
            result.m_IsRounded = false;
            result.m_IsPositive = false;
            return result;
        
        else
        
            result.m_IsNull = false;
            result.m_IsPositive = dbValue.IsPositive;
        

        if (dbValue.Precision > 28)
        
            result.m_IsRounded = true;

            if (dbValue.Precision - dbValue.Scale <= 28)
            
                var adjustedValue = SqlDecimal.AdjustScale(dbValue, 28 - dbValue.Precision, true);
                result.m_Value = adjustedValue.Value;
                result.m_IsOverflow = false;
            
            else
            
                result.m_Value = 0;
                result.m_IsOverflow = true;
            
        
        else
        
            result.m_Value = dbValue.Value;
            result.m_IsRounded = false;
        

        return result;
    

    public override string ToString()
    
        return ToString(CultureInfo.CurrentCulture);
    

    public string ToString(IFormatProvider provider)
    
        if (IsNull)
        
            return string.Empty;
        
        if (IsOverflow)
        
            return "###";
        
        return Value.ToString(provider);
    

更新:要小心 AdjustScale 方法,因为它的文档不准确 - digits 参数不是结果值中的位数,而是位数的变化(因此,要将精度降低 2,您必须通过 digits=-2)

【讨论】:

以上是关于SqlDataAdapter.Fill() 溢出异常的主要内容,如果未能解决你的问题,请参考以下文章

SqlDataAdapter.Fill(DataGridView.DataSource) 复制所有行

SqlClient.SqlDataAdapter.Fill(DataSet) 结果如果不选择查询

SqlDataAdapter.Fill 方法慢

SqlDataAdapter.Fill 方法慢

SqlDataAdapter#Fill:`SelectCommand.connection` 属性尚未初始化

SqlDataAdapter.Fill 运行 SQL 命令两次