2012-02-02 2 views
0

Я написал небольшое консольное приложение, которое я указываю на папку, содержащую файлы DBF/FoxPo.C# BulkCopy, DBF Errors (Timout & Provider не удалось определить ...)

Затем он создает таблицу в SQL на основе каждой таблицы dbf, затем выполняет массовую копию для вставки данных в SQL. Она работает довольно хорошо, по большей части, за несколько коряг кроме ..

1) Некоторые из таблиц FoxPro содержит 5000000+ записи и expries соединения до завершения вставки ..

Вот моя строка соединения :

<add name="SQL" connectionString="data source=source_source;persist security info=True;user id=DBFToSQL;password=DBFToSQL;Connection Timeout=20000;Max Pool Size=200" providerName="System.Data.SqlClient" /> 

сообщение об ошибке: «время ожидания истекло время ожидания истекло до завершения операции или сервер не отвечает.»

КОД:

using (SqlConnection SQLConn = new SqlConnection(SQLString)) 
using (OleDbConnection FPConn = new OleDbConnection(FoxString)) 
{ 
    ServerConnection srvConn = new Microsoft.SqlServer.Management.Common.ServerConnection(SQLConn); 
    try 
    { 
     FPConn.Open();      
     string dataString = String.Format("Select * from {0}", tableName); 

     using (OleDbCommand Command = new OleDbCommand(dataString, FPConn)) 
     using (OleDbDataReader Reader = Command.ExecuteReader(CommandBehavior.SequentialAccess)) 
     {      
      tbl = new Table(database, tableName, "schema"); 

      for (int i = 0; i < Reader.FieldCount; i++) 
      {       
       col = new Column(tbl, Reader.GetName(i), ConvertTypeToDataType(Reader.GetFieldType(i))); 
       col.Nullable = true; 
       tbl.Columns.Add(col);      
      } 

      tbl.Create();      
      BulkCopy(Reader, tableName); 
     }     
    } 
    catch (Exception ex) 
    { 
     // LogText(ex, @"C:\LoadTable_Errors.txt", tableName); 
     throw ex; 
    } 
    finally 
    { 
     SQLConn.Close(); 
     srvConn.Disconnect(); 
    } 
} 

private DataType ConvertTypeToDataType(Type type) 
{ 
    switch (type.ToString()) 
    { 
     case "System.Decimal": 
      return DataType.Decimal(18, 38); 
     case "System.String": 
      return DataType.NVarCharMax; 
     case "System.Int32": 
      return DataType.Int; 
     case "System.DateTime": 
      return DataType.DateTime; 
     case "System.Boolean": 
      return DataType.Bit; 
     default: 
      throw new NotImplementedException("ConvertTypeToDataType Not implemented for type : " + type.ToString()); 
    } 
} 

private void BulkCopy(OleDbDataReader reader, string tableName) 
{ 
    using (SqlConnection SQLConn = new SqlConnection(SQLString)) 
    {  
     SQLConn.Open(); 
     SqlBulkCopy bulkCopy = new SqlBulkCopy(SQLConn); 

     bulkCopy.DestinationTableName = "schema." + tableName; 

     try 
     { 
      bulkCopy.WriteToServer(reader);   
     } 
     catch (Exception ex) 
     {   
      //LogText(ex, @"C:\BulkCopy_Errors.txt", tableName); 
     } 
     finally 
     { 
      SQLConn.Close(); 
      reader.Close(); 
     } 
    } 
} 

Мой 2-й 3-й & ошибки следующие:

Я понимаю, что проблемы есть, но как исправить их я не уверен

2) «Поставщик не смог определить десятичное значение. Например, строка была только что создана, default для столбца «Десятичный» не был доступен, и потребитель еще не установил новое десятичное значение ».

3) SqlDateTime переполнение. Должно быть между 1/1/1753 12:00:00 AM и 12/31/9999 11:59:59 PM.

Я нашел результат на Google, что указано, что вопрос: [A] ... и возможная работа вокруг [B] (но я хотел бы, чтобы мои десятичные значения как десятичное и даты в качестве даты, так как я будет делать дальнейшие расчеты с данными)

Что я хочу сделать в качестве решения

1.) либо увеличить время соединения, (но я не думаю, что я могу увеличить это больше, чем у меня есть), или, альтернативно, можно разделить результаты OleDbDataReader и делать в инкрементальной вставке?

2.) Я думал, возможно ли иметь возможность массового копирования, чтобы игнорировать результаты с ошибками или иметь записи, которые делают ошибку для входа в файл csv или что-то в этом роде?

+0

Рассматривать ответ (1) - это было бы моим предложением. Сделайте это в 1000 записях. Это должно устранить проблему и, надеюсь, предотвратить проблемы с замораживанием/блокировкой. Что касается (2) - почему бы не использовать значения по умолчанию, если они отсутствуют - скажем, стиль .MinValue или, возможно, -1 для десятичной и 01/01/1980 для даты. (Это просто примеры - любые значения, соответствующие вашим потребностям, могут быть использованы). – Bertie

+0

любая идея о том, как делать инкрементное чтение? Я искал, но не везет пока ... –

ответ

1

Итак, где вы делаете «для» заявления я бы, вероятно, разбить его взять так много в то время:

int i = 0; 
int MaxCount = 1000; 

while (i < Reader.FieldCount) 
{ 
    var tbl = new Table(database, tableName, "schema"); 

    for (int j = i; j < MaxCount; j++) 
    {        
     col = new Column(tbl, Reader.GetName(j), ConvertTypeToDataType(Reader.GetFieldType(j))); 
     col.Nullable = true; 
     tbl.Columns.Add(col); 
     i++;      
    } 

    tbl.Create();       
    BulkCopy(Reader, tableName); 
} 

Таким образом, «я» следит за общим количеством, «J» держит отслеживать инкрементный счетчик (т. е. ваш максимум за один раз), а когда вы создали свою «партию», вы создаете таблицу и ее массовую копию.

Это похоже на то, что вы ожидаете?

Cheers,
Chris.

+0

Я получаю то, что вы связываете, чтобы сделать это, но это правильно ... хотя, раздел кода, который вы модифицировали, отвечает за создание столбца и таблицы. Мне нужно постепенно передавать данные в sql, что будет означать модификацию метода BulkCopy. Я отправлю свою текущую попытку в качестве ответа. –

0

Это мой текущий подход к методу массовой копии, я работаю около 90% таблиц, но я получаю выход OutOfMemory с большими таблицами ... Я хотел бы разделить данные читателя на более мелкие secions, без того, чтобы передать его в DataTable, и хранить ее в памяти первого (который является причиной OutOfMemory исключения на больших наборов результатов)

UPDATE

Imodified код ниже, чтобы как это выглядит в моем решении .. Это не красиво .. но это работает. Я сделаю несколько рефакторингов и снова обновлю ответ.

private void BulkCopy(OleDbDataReader reader, string tableName, Table table) 
    { 
     Console.WriteLine(tableName + " BulkCopy Started."); 
     try 
     { 
      DataTable tbl = new DataTable(); 
      List<Type> typeList = new List<Type>(); 
      foreach (Column col in table.Columns) 
      { 
       tbl.Columns.Add(col.Name, ConvertDataTypeToType(col.DataType)); 
       typeList.Add(ConvertDataTypeToType(col.DataType)); 
      } 

      int batch = 1; 
      int counter = 0; 

      DataRow tblRow = tbl.NewRow(); 

      while (reader.Read()) 
      { 
       counter++; 
       int colcounter = 0; 
       foreach (Column col in table.Columns) 
       { 
        try 
        { 
         tblRow[colcounter] = reader[colcounter]; 
        } 
        catch (Exception) 
        { 
         tblRow[colcounter] = GetDefault(typeList[0]); 
        } 
        colcounter++; 
       } 

       tbl.LoadDataRow(tblRow.ItemArray, true); 

       if (counter == BulkInsertIncrement) 
       { 
        Console.WriteLine(tableName + " :: Batch >> " + batch); 
        counter = PerformInsert(tableName, tbl, batch); 
        batch++; 
       } 
      } 

      if (counter > 0) 
      { 
       Console.WriteLine(tableName + " :: Batch >> " + batch); 
       PerformInsert(tableName, tbl, counter); 
      } 

      tbl = null; 
      Console.WriteLine("BulkCopy Success!"); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("BulkCopy Fail!"); 
      SharedLogger.Write(ex, @"C:\BulkCopy_Errors.txt", tableName); 
      Console.WriteLine(ex.Message); 
     } 
     finally 
     { 
      reader.Close(); 
      reader.Dispose(); 

     } 
     Console.WriteLine(tableName + " BulkCopy Ended."); 
     Console.WriteLine("*****"); 
     Console.WriteLine(""); 
    } 
+0

. Мне бы хотелось, чтобы реорганизовать код, который создает таблицу в ее собственный метод. Тогда вы могли бы вызвать это до цикла for, о котором я упомянул, так что это делается только один раз, тогда ваша массовая копия касается только копирования. Технически у вас есть метод, выполняющий две работы - подумайте о разнесении проблем. Один метод делает одну работу. – Bertie

+0

Таблица предназначена для временного хранения, так как я не могу разделить OleDbDataReader. Но спасибо за ввод. –