Comment créer une classe personnalisée avec IDataReader de LumenWorks (Fast CSV Reader) pour utiliser SqlBulkCopy avec différentes versions de fichier?

c# csv idatareader sqlbulkcopy

Question

J'ai décidé de remplacer / convertir DataTable en IDataReader en raison de problèmes de mémoire.

Après un certain temps de recherches dans Google et MSDN, je suis tombé sur http://www.michaelbowersox.com/2011/12/22/using-a-custom-idatareader-to-stream-data-into-a-database / et Bulk Insert Sql Server millions d’enregistrements .

Depuis que j'utilise LumenWorks Fast CSV Reader, je ne sais pas encore comment dire à CsvReader d’avoir IDataReader pour utiliser 2 versions de champs différentes. :-( Le csvReader.FieldCount est la clé ici mais je ne vois pas comment dire à CsvReader d'utiliser l'une des 2 nouvelles classes ayant l'interface IDataReader. Voir le script original et le script modifié ci-dessous ... Merci ...

// scripts originaux ...

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);
   }
}

// Nouveau 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"); }
        );
   }
}

// Exemple de script ...

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);
}

Réponse populaire

Depuis, ce sera un peu long, je l’écris comme une réponse.

* Je suppose que malgré le fait que vous ayez deux types de fichiers csv avec des ordres de champs différents, la table cible est la même . * [EDIT] Inutile de supposer, vous avez indiqué dans votre commentaire.

Pour pouvoir comprendre votre contexte, j'ai obtenu des exemples de données ici.

Disons que le premier type de fichier ressemble à ceci:

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

Et le second type comme (certaines colonnes sont inversées Age <-> Finish et il y a des champs supplémentaires)

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,,,,

Donc, la table cible ressemblerait à quelque chose comme (seulement des colonnes)

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

Je vois deux options (+1 option à la fin) ici:

OPTION 1

  1. Ajoutez l'étape 0 pour uniformiser tous les fichiers d'entrée au niveau du champ en définissant un format de champ. Cela peut être fait en créant les mêmes champs que vous avez sur la base de données.

[Imaginons que Test4 et Test5 sont des colonnes qui existent sur la table cible mais qui manquent dans les deux fichiers CSV]

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3,Test4,Test5
  1. Analyser tous les fichiers que vous avez (les deux types) les réécrire sur un (ou plusieurs à vous de choisir) en respectant le format que vous avez défini. De cette façon, vous n’avez qu’un (ou plusieurs) fichier avec un format unique.

  2. Vous pouvez analyser maintenant ce fichier pour l'insérer dans la base de données à l'aide du lecteur csv car le problème d'incompatibilité de champ est traité avec le dernier fichier que vous avez obtenu dans un format unique.

OPTION 2

Vous ferez deux fois l'opération SqlBulkCopy. Au premier tour, vous allez lire les fichiers contenant 48 champs et au tour suivant, les fichiers à 51 champs.

            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);
                }
            }

Et répétez la même chose avec les fichiers ayant 51 champs

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

Plus d'informations sur SqlBulkCopyColumnMapping peuvent être trouvées ici .

OPTION 3

Si vous souhaitez vous aventurer dans la création de votre lecteur de données, voici quelques liens:

Le blog de Daniel Wertheim

Un exemple d'implémentation sur codeproject

Un autre

Et enfin MSDN

NOTE PERSONNELLE Par manque de temps, pour un problème similaire, j’ai abandonné la 3ème option car, avec tous les tests unitaires et toutes les optimisations que vous devrez faire, cela peut prendre du temps ( du moins pour cas )

OPTION 4 Peut-être qu'avec le mappage de colonne que j'ai indiqué dans l'OPTION 2, vous voudriez implémenter votre chemin en testant le nombre de champs. Mais instinctivement, je suggérerais de ne pas compter les champs par des entiers codés en dur.



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi