【问题标题】:SqlBulkCopy with ObjectReader - Failed to convert parameter value from a String to a Int32带有 ObjectReader 的 SqlBulkCopy - 无法将参数值从字符串转换为 Int32
【发布时间】:2017-06-08 11:26:46
【问题描述】:

我正在使用 SqlBulkCopy (.NET) 和 ObjectReader (FastMember) 从基于 XML 的文件中执行导入。我添加了正确的列映射。

在某些情况下,我收到一个错误:无法将参数值从字符串转换为 Int32。

我想了解如何 1. 追踪失败的实际表列 2.获取ObjectReader上的“当前”

示例代码:

     using (ObjectReader reader = genericReader.GetReader())
                {
                    try
                    {
                        sbc.WriteToServer(reader); //sbc is SqlBulkCopy instance
                        transaction.Commit();
                    }
                    catch (Exception ex)
                    {
                        transaction.Rollback();                           
                    }
                }

“ex”是否携带更多信息,然后只是错误:
System.InvalidOperationException : The given value of type String from the data source cannot be converted to type int of the specified target column.

【问题讨论】:

    标签: sqlbulkcopy fastmember


    【解决方案1】:

    简单回答

    简单的答案是否定的。 .NET's SqlBulkCopy 如此之快的原因之一是它不会记录它所做的任何事情。您无法直接从.NET's SqlBulkCopy 异常中获取任何附加信息。但是,这就是说David Catriel 写了一篇关于此的文章并提供了一个可能的解决方案,您可以完全阅读有关here 的信息。

    尽管此方法可能会提供您正在寻找的答案,但我建议仅在调试时使用辅助方法,因为如果在您的代码中始终如一地运行,这很可能会对性能产生一些影响。

    为什么要使用变通方法

    缺少日志记录肯定会加快速度,但是当您 抽了几十万行突然出现故障 其中一个由于限制,您被卡住了。所有的 SqlException 会告诉你给定的东西出了问题 约束(你至少会得到约束的名称),但那是 关于它。然后你不得不回到你的源头,运行 单独的 SELECT 语句(或手动搜索),并找到 罪魁祸首行。

    除此之外,如果您已经 因为 SqlBulkCopy 得到了其中有几个潜在故障的数据 一旦遇到第一个故障,就会停止。一旦你纠正了 一、需要重新运行负载才能找到第二个错误等

    优点:

    1. 报告 SqlBulkCopy 可能遇到的所有错误

    2. 报告所有罪魁祸首数据行,以及该行可能导致的异常

    3. 整个事情在最后回滚的事务中运行,因此不会提交任何更改。

    缺点:

    1. 对于大量数据,可能需要几分钟时间。

    2. 此解决方案是反应性的;即错误不会作为 SqlBulkCopy.WriteToServer() 进程引发的异常的一部分返回。相反,在引发异常后执行此辅助方法,以尝试捕获所有可能的错误及其相关数据。这意味着,如果发生异常,您的进程将需要比仅运行批量复制更长的时间来运行。

    3. 您不能从失败的 SqlBulkCopy 重用相同的 DataReader 对象,因为读取器仅转发无法重置的消防水带。您需要创建一个相同类型的新阅读器(例如重新发出原始 SqlCommand、基于相同的 DataTable 重新创建阅读器等)。

    使用 GetBulkCopyFailedData 方法

    private void TestMethod()
    {
       // new code
       SqlConnection connection = null;
       SqlBulkCopy bulkCopy = null;
    
       DataTable dataTable = new DataTable();
       // load some sample data into the DataTable
       IDataReader reader = dataTable.CreateDataReader();
    
       try 
       {
          connection = new SqlConnection("connection string goes here ...");
          connection.Open();
          bulkCopy = new SqlBulkCopy(connection); 
          bulkCopy.DestinationTableName = "Destination table name";
          bulkCopy.WriteToServer(reader);
       }
       catch (Exception exception)
       {
          // loop through all inner exceptions to see if any relate to a constraint failure
          bool dataExceptionFound = false;
          Exception tmpException = exception;
          while (tmpException != null)
          {
             if (tmpException is SqlException
                && tmpException.Message.Contains("constraint"))
             {
                dataExceptionFound = true;
                break;
             }
             tmpException = tmpException.InnerException;
          }
    
          if (dataExceptionFound)
          {
             // call the helper method to document the errors and invalid data
             string errorMessage = GetBulkCopyFailedData(
                connection.ConnectionString,
                bulkCopy.DestinationTableName,
                dataTable.CreateDataReader());
             throw new Exception(errorMessage, exception);
          }
       }
       finally
       {
          if (connection != null && connection.State == ConnectionState.Open)
          {
             connection.Close();
          }
       }
    }
    

    GetBulkCopyFailedData() 然后打开一个到数据库的新连接, 创建一个事务,并开始批量复制数据的一行 时间。它通过阅读提供的 DataReader 和 将每一行复制到一个空的 DataTable 中。然后数据表是大容量的 复制到目标数据库,并导致任何异常 从中捕获并记录(连同导致 它),然后循环在下一行重复。在末尾 我们回滚事务并返回完整的 DataReader 错误信息。修复数据源中的问题现在应该是 微风。

    GetBulkCopyFailedData 方法

    /// <summary>
    /// Build an error message with the failed records and their related exceptions.
    /// </summary>
    /// <param name="connectionString">Connection string to the destination database</param>
    /// <param name="tableName">Table name into which the data will be bulk copied.</param>
    /// <param name="dataReader">DataReader to bulk copy</param>
    /// <returns>Error message with failed constraints and invalid data rows.</returns>
    public static string GetBulkCopyFailedData(
       string connectionString,
       string tableName,
       IDataReader dataReader)
    {
       StringBuilder errorMessage = new StringBuilder("Bulk copy failures:" + Environment.NewLine);
       SqlConnection connection = null;
       SqlTransaction transaction = null;
       SqlBulkCopy bulkCopy = null;
       DataTable tmpDataTable = new DataTable();
    
       try
       { 
          connection = new SqlConnection(connectionString); 
          connection.Open();
          transaction = connection.BeginTransaction();
          bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.CheckConstraints, transaction);
          bulkCopy.DestinationTableName = tableName;
    
          // create a datatable with the layout of the data.
          DataTable dataSchema = dataReader.GetSchemaTable();
          foreach (DataRow row in dataSchema.Rows)
          {
             tmpDataTable.Columns.Add(new DataColumn(
                row["ColumnName"].ToString(), 
                (Type)row["DataType"]));
          }
    
          // create an object array to hold the data being transferred into tmpDataTable 
          //in the loop below.
          object[] values = new object[dataReader.FieldCount];
    
          // loop through the source data
          while (dataReader.Read())
          {
             // clear the temp DataTable from which the single-record bulk copy will be done
             tmpDataTable.Rows.Clear();
    
             // get the data for the current source row
             dataReader.GetValues(values);
    
             // load the values into the temp DataTable
             tmpDataTable.LoadDataRow(values, true);
    
             // perform the bulk copy of the one row
             try
             {
                bulkCopy.WriteToServer(tmpDataTable);
             }
             catch (Exception ex)
             {
                // an exception was raised with the bulk copy of the current row. 
                // The row that caused the current exception is the only one in the temp 
                // DataTable, so document it and add it to the error message.
                DataRow faultyDataRow = tmpDataTable.Rows[0];
                errorMessage.AppendFormat("Error: {0}{1}", ex.Message, Environment.NewLine);
                errorMessage.AppendFormat("Row data: {0}", Environment.NewLine);
                foreach (DataColumn column in tmpDataTable.Columns)
                {
                   errorMessage.AppendFormat(
                      "\tColumn {0} - [{1}]{2}", 
                      column.ColumnName, 
                      faultyDataRow[column.ColumnName].ToString(), 
                      Environment.NewLine);
                }
             }
          }
       }
       catch (Exception ex) 
       { 
          throw new Exception(
             "Unable to document SqlBulkCopy errors. See inner exceptions for details.", 
             ex); 
       }
       finally
       {
          if (transaction != null)
          {
             transaction.Rollback();
          }
          if (connection.State != ConnectionState.Closed)
          {
             connection.Close();
          }
       }
       return errorMessage.ToString();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-09
      • 2014-08-24
      • 2012-09-30
      相关资源
      最近更新 更多