Come creare una classe personalizzata w / IDataReader da LumenWorks (Fast CSV Reader) per utilizzare SqlBulkCopy pur avendo diverse versioni di file differenti?

c# csv idatareader sqlbulkcopy

Domanda

Ho deciso di sostituire / convertire DataTable in IDataReader a causa di problemi di memoria.

Dopo un po 'di ricerche su Google e MSDN, mi sono imbattuto in questo http://www.michaelbowersox.com/2011/12/22/using-a-custom-idatareader-to-stream-data-into-a-database / e Bulk Insert Sql Server milioni di record .

Dal momento che sto usando LumenWorks Fast CSV Reader e non ho ancora capito come dire a CsvReader di avere IDataReader per usare 2 diverse versioni di campo. :-( Il csvReader.FieldCount è la chiave qui, ma non vedo come dire a CsvReader di usare una delle due nuove classi con l'interfaccia IDataReader.Vedi lo script originale e lo script modificato sotto ... Grazie ...

// Script originali ...

var dbConnection = new SqlConnection(_dbConnectionString);

using (var dbBulkCopy = new SqlBulkCopy(dbConnection)
{
   using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\\', '#', ValueTrimmingOptions.UnquoteOnly))
   {
       while(csvReader.ReadNextRecord())
       {
           if (csvReader.FieldCount == 48)
           {
               //Version 1...
               dataRow["DealerId"] = csvReader[0];
               dataRow["DealerName"] = csvReader[1];
               //Etc...
           }
           else if (csvReader.FieldCount == 51)
           {
               //Version 2...
               dataRow["DealerId"] = csvReader[0];
               dataRow["DealerName"] = csvReader[1];
               //Etc...
           }
           else { throw new Exception("Field Total Count Mismatched"); }

           dataTable.Rows.Add(dataRow);
       }

       dbConnection.Open();

       dbBulkCopy.WriteToServer(dataTable);
   }
}

// Nuovo script ...

 var dbConnection = new SqlConnection(_dbConnectionString);

using (var dbBulkCopy = new SqlBulkCopy(dbConnection)
{
   dbConnection.Open();

   using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\\', '#', ValueTrimmingOptions.UnquoteOnly))
   {
       csvReader.ReadNextRecord();

       dbBulkCopy.WriteToServer(
           if (csvReader.FieldCount == 48)
           {
               //Version 1...

               csvReader....???  //Assign a custom class having IDataTable...
           }
           else if (csvReader.FieldCount == 51)
           {
               //Version 2...
               csvReader....???  //Assign a custom class having IDataTable...
           }
           else { throw new Exception("Field Total Count Mismatched"); }
        );
   }
}

// Script di esempio ...

using (var file = new StreamReader(path))
using (var csv = new CsvReader(file, true)) // true = has header row
using (var bcp = new SqlBulkCopy(connection)) {
    bcp.DestinationTableName = "TableName";
    bcp.WriteToServer(csv);
}

Risposta popolare

Poiché, sarà un po 'lungo, lo scrivo come risposta.

* Suppongo che nonostante il fatto che tu abbia due tipi di file csv che arrivano con ordini di campo diversi, la tabella di destinazione è la stessa . * [EDIT] Non c'è bisogno di assumere, hai indicato nel tuo commento.

Per essere in grado di capire il tuo contesto, ho preso alcuni dati di esempio da qui.

Diciamo che il primo tipo di file è qualcosa di simile al seguente:

Rk,Year,Age,Tm,Lg,Id,G,W,L,W-L,Finish,
1,1978,37,Atlanta Braves,NL,,162,69,93,.426,6

E il secondo tipo (alcune colonne sono invertite Età <-> Fine e ci sono campi aggiuntivi)

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3
1,1978,Very good year,Atlanta Braves,NL,,162,69,93,.426,96,,,,

Quindi la tabella di destinazione sarebbe simile a qualcosa (solo colonne)

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3

Vedo due opzioni (opzione +1 alla fine) qui:

OPZIONE 1

  1. Aggiungi il passaggio 0 per uniformare tutti i file di input a livello di campo definendo un formato di campo. Questo può essere fatto creando gli stessi campi che hai sul database.

[Immaginiamo che Test4 e Test5 siano colonne che esistono sulla tabella di destinazione ma mancano in entrambi i file CSV]

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3,Test4,Test5
  1. Analizza tutti i file che hai (entrambi i tipi) li riscrivi su uno (o su più tuoi) rispettando il formato che hai definito. In questo modo, hai solo 1 (o più) file con un formato unico.

  2. È possibile analizzare ora questo file per inserirlo nel database utilizzando il lettore csv poiché il problema di incompatibilità di campo viene gestito con l'ultimo file ottenuto in formato univoco.

OPZIONE 2

Farai l'operazione SqlBulkCopy due volte. Il primo round leggerete i file con 48 campi e il prossimo round leggerete i file con 51 campi.

            var FilesWith48Fields = Directory.GetFiles(@"D:\Data\48Fields", "*.csv");

            foreach (var fileName in FilesWith48Fields)
            {
                using (var file = new StreamReader(fileName))
                using (var csv = new CsvReader(file, true)) // true = has header row
                using (var bcp = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepNulls))
                {
                    bcp.DestinationTableName = "fletchsodTable";
                    // map the first field of your CSV to the column in Database
                    var mapping1 = new SqlBulkCopyColumnMapping(0, "FirstColumnName");
                    bcp.ColumnMappings.Add(mapping1);

                    var mapping2 = new SqlBulkCopyColumnMapping(1, "SecondColumnName");
                    bcp.ColumnMappings.Add(mapping2);  
                    ....

                    bcp.WriteToServer(csv);
                }
            }

E ripeti lo stesso con i file che hanno 51 campi

var FilesWith51Fields = Directory.GetFiles(@"D:\Data\51Fields", "*.csv");
......

Maggiori informazioni su SqlBulkCopyColumnMapping possono essere trovate qui .

OPZIONE 3

Nel caso in cui tu voglia avventurarti nella creazione del tuo Data Reader, ecco alcuni link:

Il blog di Daniel Wertheim

Un esempio di impianto su codeproject

Un altro

E infine MSDN

NOTA PERSONALE A causa della mancanza di tempo, per un problema simile ho rinunciato alla terza opzione perché con tutti i test unitari e l'ottimizzazione che dovrai fare e altre modifiche, questo può richiedere del tempo ( almeno per me, è stato il caso )

OPZIONE 4 Forse, con la mappatura della colonna che ho indicato nell'OPZIONE 2, vorresti implementare il tuo modo testando i conteggi dei campi. Ma istintivamente, suggerirei di non contare i campi con numeri interi hardcoded.



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow