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
相关产品推荐
相关产品推荐

