2016-08-18 2 views
0

Хорошо, я попытаюсь объяснить это как можно лучше. Я написал приложение, которое использует таблицу SQL для определения структуры источника данных с фиксированной шириной (так, заголовок, начальный индекс, длина поля и т. Д.). Когда мое приложение запускается, оно запрашивает эту таблицу и создает объект DataTable (назовите его finalDT) с объектами DataColumn, содержащими столбец ColumnName =. Затем я добавляю в эту таблицу набор объектов DataColumn, которые существуют в каждом используемом нами источнике данных (который я называю производными столбцами). Я также создаю поле первичного ключа, которое является автоматически увеличивающимся целым числом. Первоначально я перекатил свое собственное решение, чтобы прочитать файл фиксированной ширины, но пытаюсь преобразовать его в FileHelper. В основном, я хочу включить его, чтобы иметь доступ к другим типам файлов, которые может обрабатывать FileHelper (CSV, Excel и т. Д.).Использование FileHelpers.Dynamic, чтение файла фиксированной ширины и загрузка на SQL

Теперь, моя проблема. Используя FileHelper.Dynamic, я был в состоянии создать объект FileHelperEngine, используя следующий метод:

private static FileHelperEngine GetFixedWidthFileClass(bool ignore) 
{ 
    singletonArguments sArgs = singletonArguments.sArgs; 
    singletonSQL sSQL = singletonSQL.sSQL; 
    List<string> remove = new List<string>(); 

    FixedLengthClassBuilder flcb = new FixedLengthClassBuilder(sSQL.FixedDataDefinition.DataTableName); 
    flcb.IgnoreFirstLines = 1; 
    flcb.IgnoreLastLines = 1; 
    flcb.IgnoreEmptyLines = true; 

    foreach (var dcs in sSQL.FixedDataDefinition.Columns) 
    { 
     flcb.AddField(dcs.header, Convert.ToInt32(dcs.length), "String"); 

     if (ignore && dcs.ignore) 
     { 
      flcb.LastField.FieldValueDiscarded = true; //If we want to ignore a column, this is how to do it. Would like to incorporate this. 
      flcb.LastField.Visibility = NetVisibility.Protected; 
     } 
     else 
     { 
      flcb.LastField.TrimMode = TrimMode.Both; 
      flcb.LastField.FieldNullValue = string.Empty; 
     } 
    } 

    return new FileHelperEngine(flcb.CreateRecordClass()); 
} 

sSQL.FixedDataDefinition.Columns как я храню определение полей для источника данных файла фиксированной ширины. Затем я генерировать DataTable, выполнив:

DataTable dt = engine.ReadFileAsDT(file); 

, где находится file полный путь к фиксированной ширины файла и engine в котором хранятся результаты от метода GetFixedWidthFileClass() показанного выше. Хорошо, теперь у меня есть DataTable без первичного ключа и ни одного из полученных столбцов. Кроме того, все поля в dt отмечены как ReadOnly = true. Здесь все становится беспорядком.

Мне нужно набрать dt в finalDT, и с dt должно быть в порядке, не имея информации о главном ключе. Если это может произойти, я могу использовать finalDT для загрузки моих данных в таблицу SQL. Если этого не может быть, тогда мне нужен способ для finalDT, чтобы не иметь Первичного ключа, но все равно загружайте его в таблицу SQL. Будет ли SqlBulkCopy разрешить это? Есть ли другой способ?

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

ответ

1

Я понял. Это некрасиво, но вот как это работает. В принципе, как я настраиваю свой код в моем исходном сообщении, все еще применяется, поскольку я ничего не изменил в методе GetFixedWidthFileClass(). Затем мне пришлось добавить два метода, чтобы получить finalDT настройки правильно:

/// <summary> 
///  For a given a datasource file, add all rows to the DataSet and collect Hexdump data 
/// </summary> 
/// <param name="ds"> 
///  The <see cref="System.Data.DataSet" /> to add to 
/// </param> 
/// <param name="file"> 
///  The datasource file to process 
/// </param> 
internal static void GenerateDatasource(ref DataSet ds, ref FileHelperEngine engine, DataSourceColumnSpecs mktgidSpecs, string file) 
{ 
    // Some singleton class instances to hold program data I will need. 
    singletonSQL sSQL = singletonSQL.sSQL; 
    singletonArguments sArgs = singletonArguments.sArgs; 

    try 
    { 
     // Load a DataTable with contents of datasource file. 
     DataTable dt = engine.ReadFileAsDT(file); 

     // Clean up the DataTable by removing columns that should be ignored. 
     DataTableCleanUp(ref dt, ref engine); 

     // ReadFileAsDT() makes all of the columns ReadOnly. Fix that. 
     foreach (DataColumn column in dt.Columns) 
      column.ReadOnly = false; 

     // Okay, now get a Primary Key and add in the derived columns. 
     GenerateDatasourceSchema(ref dt); 

     // Parse all of the rows and columns to do data clean up and assign some custom 
     // values. Add custom values for jobID and serial columns to each row in the DataTable. 
     for (int row = 0; row < dt.Rows.Count; row++) 
     { 
      string version = string.Empty; // The file version 
      bool found = false; // Used to get out of foreach loops once the required condition is found. 

      // Iterate all configured jobs and add the jobID and serial number to each row 
      // based upon match. 
      foreach (JobSetupDetails job in sSQL.VznJobDescriptions.JobDetails) 
      { 
       // Version must match id in order to update the row. Break out once we find 
       // the match to save time. 
       version = dt.Rows[row][dt.Columns[mktgidSpecs.header]].ToString().Trim().Split(new char[] { '_' })[0]; 
       foreach (string id in job.ids) 
       { 
        if (version.Equals(id)) 
        { 
         dt.Rows[row][dt.Columns["jobid"]] = job.jobID; 

         lock (locklist) 
          dt.Rows[row][dt.Columns["serial"]] = job.serial++; 

         found = true; 
         break; 
        } 
       } 
       if (found) 
        break; 
      } 

      // Parse all columns to do data clean up. 
      for (int column = 0; column < dt.Columns.Count; column++) 
      { 
       // This tab character keeps showing up in the data. It should not be there, 
       // but customer won't fix it, so we have to. 
       if (dt.Rows[row][column].GetType() == typeof(string)) 
        dt.Rows[row][column] = dt.Rows[row][column].ToString().Replace('\t', ' '); 
      } 
     } 

     dt.AcceptChanges(); 

     // DataTable is cleaned up and modified. Time to push it into the DataSet. 
     lock (locklist) 
     { 
      // If dt is writing back to the DataSet for the first time, Rows.Count will be 
      // zero. Since the DataTable in the DataSet does not have the table schema and 
      // since dt.Copy() is not an option (ds is referenced, so Copy() won't work), Use 
      // Merge() and use the option MissingSchemaAction.Add to create the schema. 
      if (ds.Tables[sSQL.FixedDataDefinition.DataTableName].Rows.Count == 0) 
       ds.Tables[sSQL.FixedDataDefinition.DataTableName].Merge(dt, false, MissingSchemaAction.Add); 
      else 
      { 
       // If this is not the first write to the DataSet, remove the PrimaryKey 
       // column to avoid duplicate key values. Use ImportRow() rather then .Merge() 
       // since, for whatever reason, Merge() is overwriting ds each time it is 
       // called and ImportRow() is actually appending the row. Ugly, but can't 
       // figure out another way to make this work. 
       dt.PrimaryKey = null; 
       dt.Columns.Remove(dt.Columns[0]); 
       foreach (DataRow dr in dt.Rows) 
        ds.Tables[sSQL.FixedDataDefinition.DataTableName].ImportRow(dr); 
      } 

      // Accept all the changes made to the DataSet. 
      ds.Tables[sSQL.FixedDataDefinition.DataTableName].AcceptChanges(); 
     } 

     // Clean up memory. 
     dt.Clear(); 

     // Log my progress. 
     log.GenerateLog("0038", log.Info 
         , engine.TotalRecords.ToString() + " DataRows successfully added for file:\r\n\t" 
         + file + "\r\nto DataTable " 
         + sSQL.FixedDataDefinition.DataTableName); 
    } 
    catch (Exception e) 
    { 
     // Something bad happened here. 
     log.GenerateLog("0038", log.Error, "Failed to add DataRows to DataTable " 
         + sSQL.FixedDataDefinition.DataTableName 
         + " for file\r\n\t" 
         + file, e); 
    } 
    finally 
    { 
     // Successful or not, get rid of the datasource file to prevent other issues. 
     File.Delete(file); 
    } 
} 

И этот метод:

/// <summary> 
///  Deletes columns that are not needed from a given <see cref="System.Data.DataTable" /> reference. 
/// </summary> 
/// <param name="dt"> 
///  The <see cref="System.Data.DataTable" /> to delete columns from. 
/// </param> 
/// <param name="engine"> 
///  The <see cref="FileHelperEngine" /> object containing data field usability information. 
/// </param> 
private static void DataTableCleanUp(ref DataTable dt, ref FileHelperEngine engine) 
{ 
    // Tracks DataColumns I need to remove from my temp DataTable, dt. 
    List<DataColumn> removeColumns = new List<DataColumn>(); 

    // If a field is Discarded, then the data was not imported because we don't need this 
    // column. In that case, mark the column for deletion by adding it to removeColumns. 
    for (int i = 0; i < engine.Options.Fields.Count; i++) 
     if (engine.Options.Fields[i].Discarded) 
      removeColumns.Add(dt.Columns[i]); 

    // Reverse the List so changes to dt don't generate schema errors. 
    removeColumns.Reverse(); 

    // Do the deletion. 
    foreach (DataColumn column in removeColumns) 
     dt.Columns.Remove(column); 

    // Clean up memory. 
    removeColumns.Clear(); 
} 

В принципе, так как ds (набор данных, где finalDT жизни) сослались в методе GenerateDatasource, я не мог использовать dt.Copy() для ввода данных в него. Для этого мне пришлось использовать Merge(). Тогда, когда мне бы хотелось использовать Merge(), мне пришлось использовать цикл foreach и ImportRow(), потому что Merge() переписывал finalDT.

Другие вопросы, которые я должен был исправить были:

  1. Когда я использую ImportRow(), то мне также нужно удалить PrimaryKey из dt или иначе я получаю сообщения об ошибках, дублированных ключей.
  2. FileHelperEngine или FileHelpers.Dynamic.FixedLengthClassBuilder есть проблема с прыжком мимо столбцов, которые я хочу игнорировать. Он либо не признает их вообще, тем самым убивая смещение столбца и, впоследствии, точность в отношении того, как данные считываются в файле данных (с использованием опции FieldHidden), или он читает их и создает столбцы в любом случае, t загружать данные (используя параметры FieldValueDiscarded и Visibility.Private или .Protected). Для меня это значит, что мне пришлось перебирать dt после звонка в engine.ReadFileAsDT(file) и удалять столбцы, помеченные как Discarded.
  3. Поскольку FileHelper ничего не знает о моем столбце PrimaryKey или других производных столбцах, которые были добавлены ко всем моим источникам данных во время обработки, мне пришлось передать dt методу (GenerateDatasourceSchema()), чтобы отсортировать его. Этот метод в основном просто добавляет эти столбцы и гарантирует, что PrimaryKey является первым столбцом.

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

Это некрасиво, и я надеюсь найти лучший путь по дороге. Если у кого-то есть данные о том, как я это сделал, я бы с удовольствием это услышал.

Смежные вопросы