由于 DBNulls 而转换数据类型时出错

Error while converting Datatype due to DBNulls

提问人:Tom 提问时间:11/10/2023 最后编辑:derloopkatTom 更新时间:11/10/2023 访问量:78

问:

我正在尝试通过从一个数据库调用存储过程来使用我的 c# 应用程序加载数据,然后需要将该数据插入到另一个数据库中。我打算使用它的 SQL 批量插入。我确实提出了 SSIS,但不幸的是,他们没有软件和基础设施。我需要转换某些列的数据类型,因为当我批量插入目标表时它会爆炸。我正在使用函数进行转换,但不幸的是在尝试转换值时出现错误。当我尝试在转换前检查值时,它似乎没有进入 If 条件块。有人能告诉我我哪里出了问题吗?DBNullDBNull

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;
    }
}
C# sql-server ado.net dbnull

评论

0赞 derloopkat 11/10/2023
dt.Columns.Add(dc);添加对 but 对象的引用被创建到语句中并被释放。我还没有测试过这段代码,但我建议在离开代码块后调试并检查是否包含新添加的列。dcusingdt.Columnsusing{ ... }
1赞 Alexander Petrov 11/10/2023
if (dr[dc.ColumnName]应该是 ?if (dr[columnName]
0赞 Joel Coehoorn 11/10/2023
如果你在这里读过我的很多帖子,你就会知道我喜欢积木。但是这个问题中的DataColumn是我跳过它的情况。原因是在 DataTable 也被释放之前,我们并不真正想要释放该列,并且我们可以有合理的信心(即使 DataTable.Dispose() 方法实际上没有记录)DataTable 将正确释放其任何成员列对象发生这种情况。using
0赞 Joel Coehoorn 11/10/2023
此外,IIRC,您不能信任使用 或直接与 DBNull.Value 进行比较。您必须使用 或==!=DBNull.Value.Equals()IsDBNull()
0赞 Randy in Marin 11/10/2023
DataRow.IsNull 方法?learn.microsoft.com/en-us/dotnet/api/......

答:

0赞 Joel Coehoorn 11/10/2023 #1

这一行是有问题的:

if (dr[dc.ColumnName] != DBNull.Value)

原因有两方面。首先,与使用 and 运算符相比,可能会发生奇怪的事情。这样会更好:DBNull.Value!===

if (!DBNull.Value.Equals(dr[dc.ColumnName]))

此外,上面的代码正在查看尚未设置值的新列名称。此时我们仍然需要引用旧列,因此我们想要:

if (!DBNull.Value.Equals(dr[columnName]))

评论

0赞 Tom 11/10/2023
您建议的这种检查肯定更好。我收到错误,说对象应该实现 IConvertible 。所以我更新了我的帖子,以展示我是如何做到的。不确定如何调用该方法
0赞 Tom 11/10/2023
如何称呼此表。ConvertColumnType<我在这里传递什么?>(“Original_Qty”, typeof(int));
0赞 Joel Coehoorn 11/10/2023
@Tom泛型必须在编译时解析。在那里没有办法使用运行时类型实例。您可能需要 switch 语句或类似语句来为 DataTable 支持的每种类型调用单独的方法,或者在一个方法中调用一个开关。但是你可以指定类型名称,即问题是它是需要可转换的旧类型table.ConvertColumnType<int>("Original_Qty", typeof(int));
0赞 Tom 11/10/2023
如果我按表打电话。ConvertColumnType<int>(“Original_Qty”, typeof(int)) ,我收到错误 int 不能用作参数 没有从“int”到“T”的装箱转换
0赞 Tom 11/10/2023
我不太确定您的意思是否需要在一个方法中使用 switch 语句。我需要在 switch 语句中检查什么