带ObjectReader的SqlBulkCopy - 无法将参数值从String转换为Int32

时间:2017-06-08 11:26:47

标签: sqlbulkcopy fastmember

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

在某些情况下,我收到错误:无法将参数值从String转换为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.

1 个答案:

答案 0 :(得分:0)

简单回答

简单的答案是否定的。 .NET's SqlBulkCopy如此之快的原因之一是它不记录它所做的任何事情。您无法直接从.NET's SqlBulkCopy例外获取任何其他信息。但是,David Catriel已经撰写了一篇关于此问题的文章并提供了一个可以解决的问题,您可以完全阅读here

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

为什么要使用

  

缺少伐木肯定会加快速度,但是当你的时候   抽了几十万行然后突然失败了   其中一个是因为一个约束,你被困住了。一切   SqlException会告诉你一个给定的东西出了什么问题   约束(你至少得到约束的名字),但是那个   关于它。然后你不得不回到你的来源,跑   在其上单独的SELECT语句(或手动搜索),并找到   罪魁祸首就是你自己。

     

最重要的是,如果你这样做,它可能是一个非常漫长而且反复的过程   由于SqlBulkCopy,因此得到了几个潜在故障的数据   一旦第一次失败就会停止。一旦你纠正了   一,你需要重新运行负载以找到第二个错误等。

优势:

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

  2. 报告所有罪魁祸首数据行,以及行将导致的例外情况

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

  4. <强>缺点:

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

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

    3. 您无法从失败的SqlBulkCopy中重用相同的DataReader对象,因为读取器只能转发无法重置的消防软管。您需要创建一个相同类型的新阅读器(例如,重新发布原始SqlCommand,根据相同的DataTable重新创建阅读器等)。

    4. 使用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()然后打开一个到数据库的新连接,   创建一个事务,并开始批量复制a中的一行数据   时间。它通过读取提供的DataReader来实现   将每一行复制到一个空的DataTable中。然后DataTable是批量的   复制到目标数据库,以及产生的任何异常   从中捕获,记录(以及导致的DataRow)   然后循环再次与下一行重复。在末尾   我们回滚事务并返回完整的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();