You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#数据转换遇DBNull值报错,判断逻辑未生效求排查

问题描述

我通过C#程序调用存储过程从一个数据库加载数据,再插入到另一个数据库,计划采用SQL批量插入方案。此前曾提议使用SSIS,但相关方缺乏对应的软件与基础设施。执行批量插入到目标表时,部分列因数据类型不匹配报错,因此我使用自定义函数转换列类型,但在转换过程中遇到DBNull值转换错误,且事前针对DBNull值的判断逻辑并未进入分支。请帮忙排查问题所在?

源程序代码

using (SqlConnection con = new SqlConnection(_SourceConnectionString))
{
    // con.Open();

    // Create a table with some rows. 
    DataTable table = new DataTable();

    var cmd = new SqlCommand("[EMR].[spOrderDailySummary]", con);
    var da = new SqlDataAdapter(cmd);

    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@OrderDate", SqlDbType.DateTime).Value = _businessDate;

    try
    {
        // LogManager.Log(LogSeverity.Info, $"Initiating report {rgc.ReportType}.");
        da.Fill(table);

        table.ConvertColumnType("Original_Qty", typeof(int));
        table.ConvertColumnType("Remaining_Qty", typeof(int));
        table.ConvertColumnType("Price", typeof(decimal));
        table.ConvertColumnType("OrderDate", typeof(DateTime));
        table.ConvertColumnType("Valid_to_Value", typeof(DateTime));
        table.ConvertColumnType("ClientID", typeof(int));
        table.ConvertColumnType("Trigger_Price", typeof(decimal));
        table.ConvertColumnType("Investment_Decision_Within_Firm", typeof(int));
        table.ConvertColumnType("Order_Priority_Timestamp", typeof(DateTime));
        table.ConvertColumnType("Transaction_Time", typeof(DateTime));
        table.ConvertColumnType("NewOrdDateTime", typeof(DateTime));

        StartDataMigration(table);
    }
}

自定义转换函数代码

public static void ConvertColumnType<T>(this DataTable dt, string columnName, Type newType) where T : Type, IConvertible
{
    using (DataColumn dc = new DataColumn(columnName + "_new", newType))
    {
        // Add the new column which has the new type, and move it to the ordinal of the old column
        int ordinal = dt.Columns[columnName].Ordinal;
        dt.Columns.Add(dc);
        dc.SetOrdinal(ordinal);

        // Get and convert the values of the old column, and insert them into the new
        foreach (DataRow dr in dt.Rows)
            if (dr[dc.ColumnName] != DBNull.Value)
            {
                dr[dc.ColumnName] = Convert.ChangeType(dr[columnName], newType);
            }

        // Remove the old column
        dt.Columns.Remove(columnName);

        // Give the new column the old column's name
        dc.ColumnName = columnName;
    }
}
问题排查与修复

核心问题分析

  • 判断对象完全搞反:转换函数中判断的是新列dr[dc.ColumnName]是否为DBNull,但新列刚创建时所有行默认都是DBNull,导致判断逻辑完全失效——本该跳过的原列DBNull值反而被执行转换,直接引发错误。
  • 泛型约束冗余且错误:泛型参数T在函数内未被使用,且Type本身不实现IConvertible,约束条件完全无效,属于多余代码。
  • 缺少转换异常处理:即使原列不是DBNull,也可能存在无法转换为目标类型的值(比如字符串"abc"转int),缺少异常处理会导致程序直接崩溃。

修复后的转换函数

public static void ConvertColumnType(this DataTable dt, string columnName, Type newType)
{
    // 先验证原列是否存在,避免后续报错
    if (!dt.Columns.Contains(columnName))
        throw new ArgumentException($"列 {columnName} 在DataTable中不存在");

    using (DataColumn dc = new DataColumn(columnName + "_new", newType))
    {
        int ordinal = dt.Columns[columnName].Ordinal;
        dt.Columns.Add(dc);
        dc.SetOrdinal(ordinal);

        foreach (DataRow dr in dt.Rows)
        {
            // 正确判断原列的值是否为DBNull
            if (dr[columnName] != DBNull.Value)
            {
                try
                {
                    dr[dc.ColumnName] = Convert.ChangeType(dr[columnName], newType);
                }
                catch (Exception ex)
                {
                    // 可根据需求记录错误日志,或设置默认值
                    dr[dc.ColumnName] = DBNull.Value;
                    // 示例日志代码:LogManager.Log(LogSeverity.Error, $"转换列 {columnName} 的值 {dr[columnName]} 失败: {ex.Message}");
                }
            }
            else
            {
                // 原列为空时,新列也设置为DBNull
                dr[dc.ColumnName] = DBNull.Value;
            }
        }

        dt.Columns.Remove(columnName);
        dc.ColumnName = columnName;
    }
}

额外优化建议

  • 在da.Fill(table)后,可先检查table.Rows.Count > 0,避免空表的无效转换操作。
  • 针对日期类型转换,建议使用DateTime.ParseExact指定明确格式,避免因系统区域设置差异导致的转换失败。

内容的提问来源于stack exchange,提问作者Tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 18:55:15