SqlBulkCopy with ObjectReader - 매개 변수 값을 String에서 Int32로 변환하지 못했습니다.

fastmember sqlbulkcopy

문제

나는 SqlBulkCopy (.NET)를 ObjectReader (FastMember)와 함께 사용하여 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();                           
                    }
                }

"예"는 더 많은 정보를 담고 있으며 그 다음 오류 만 나타냅니다 :
System.InvalidOperationException : The given value of type String from the data source cannot be converted to type int of the specified target column.

인기 답변

간단한 답변

간단한 대답은 아니오입니다. .NET's SqlBulkCopy 가 너무 빠른 이유 중 하나는 그것이 아무것도 기록하지 않는다는 것입니다. .NET's SqlBulkCopy 예외에서 직접 추가 정보를 얻을 수는 없습니다. 그러나 David Catriel 은 이에 관한 기사를 썼으며 여기에서 완전히 읽을 수있는 해결책을 제시 했다 .

비록이 메소드가 당신이 찾고있는 해답을 제공 할지라도, 디버깅 할 때 헬퍼 메소드를 사용할 것을 제안한다. 코드 내에서 일관성있게 실행된다면 성능에 영향을 미칠 수 있기 때문이다.

주변에서 일하는 이유

로깅 부족으로 인해 작업 속도가 빨라지지만 제약 조건 때문에 수십만 개의 행을 펌핑 할 때 갑자기 실패가 발생하면 문제가 발생합니다. 모든 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에 복사하면됩니다. 그런 다음 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();


아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.