2010-11-12 26 views
4

Actuellement, j'écris une méthode pour lire les données d'un fichier CSV et les importer dans une table SQL.SqlBulkCopy lève System.FormatException lors de l'exécution de WriteToServer (DataTable)

 DataTable dt = new DataTable(); 
     String line = null; 
     int i = 0; 

     while ((line = reader.ReadLine()) != null) 
     { 
      String[] data = line.Split(','); 
      if (data.Length > 0) 
      { 
       if (i == 0) 
       { 
        foreach (object item in data) 
        { 
         DataColumn c = new DataColumn(Convert.ToString(item)); 
         if (Convert.ToString(item).Contains("DATE")) 
         { 
          c.DataType = System.Type.GetType("System.DateTime"); 
         } 
         else { c.DataType = System.Type.GetType("System.String"); } 
         dt.Columns.Add(c); 
        } 
        i++; 
       } 
       else 
       { 
        DataRow row = dt.NewRow(); 
        for (int j = 0; j < data.Length; j++) 
        { 
         if (dt.Columns[j].DataType == System.Type.GetType("System.DateTime")) 
         { 
          row[j] = Convert.ToDateTime(data[j]); 
         } 
         else 
         { 
          row[j] = data[j]; 
         } 
        } 
        dt.Rows.Add(row); 
       } 
      } 
     } 
     SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings[Constant.CONNECTION_STRING_NAME].ConnectionString); 
     SqlBulkCopy s = new SqlBulkCopy(con); 
     s.DestinationTableName = "abc"; 
     con.Open(); 
     s.WriteToServer(dt); 

Le problème lors de l'exécution de cette méthode, une exception est toujours jeté à s.WriteToServer (dt);

System.FormatException: La chaîne n'a pas été reconnue en tant que DateTime valide. Il existe un mot inconnu commençant à l'index 0.

J'ai débogué et j'ai vu que toutes les données étaient correctement chargées dans DataTable. Voici un exemple d'une ligne de données dans mon fichier CSV

DATA_VENDOR_ID,DATA_VENDOR_SUB_ID,DATA_VENDOR_CLIENT_ID,DATA_VENDOR_ACTIVITY_CODE,ACTIVITY_NAME,EFFECTIVE_DATE,ACTIVITY_LEVEL1,ACTIVITY_LEVEL2,ACTIVITY_LEVEL3,ACTIVITY_LEVEL4,ACTIVITY_LEVEL5,PARTICIPANT_ID,DATA_VENDOR_ALT_ID,FILE_CREATION_DATE,INC_VALUE  
V01,,22097,ABCD01,Physical Activity,10/01/2010,Entertain Kiosk,ABCD - EFG 54,30,,AB01,W1234567891,,08/07/2006,100 

et mon schéma de table SQL:

RowID int Unique/AutoIncrement 
DataVendorId varchar(32) 
DataVendorSubId varchar(32) 
DataVendorClientId varchar(32) 
DataVendorActivityCode varchar(32) 
ActivityName varchar(64) 
EffectiveDate datetime  
ActivityLevel1 varchar(253)  
ActivityLevel2 varchar(253)  
ActivityLevel3 varchar(253)  
ActivityLevel4 varchar(253)  
ActivityLevel5 varchar(253)  
ParticipantID varchar(32) 
DataVendorAltId varchar(32) 
FileCreationDate datetime  
IncValue varchar(5) 
CreatedDate datetime optional/allow null 
ModifiedDate datetime optional/allow null 
+1

BTW, il est ** ** beaucoup plus efficace d'utiliser 'typeof (string)' 'que système .Type.GetType ("System.String") '- ditto' typeof (DateTime) ' –

+0

merci beaucoup pour votre recommandation :) – Leo

Répondre

2

Le premier problème que je vois est que vous allez avoir des problèmes avec le RowID colonne; Je m'attends à ce qu'il essaye de compenser vos données par une colonne en ce moment - il ne sait pas que vous l'omettez. Vous pouvez soit jouer avec les mappages, soit (dans votre table de données) ajouter une colonne RowID (à l'index 0) - mais notez que SQL Server ignorera les valeurs sauf si vous activez l'insertion d'identité.

Peut-être essayer une conversion datetime plus explicite:

row[j] = DateTime.ParseExact(data[j], "dd/MM/yyyy", CultureInfo.InvariantCulture); 

Notez que je ne peux pas dire à partir des données si cela est dd/MM ou MM/dd, vous devrez peut-être modifier cela.

+0

Vous avez raison, j'ai essayé d'ajouter une colonne fictive pour RowID et cela a fonctionné! – Leo

0

Les champs dans votre fichier de données ne correspond pas à la table, à savoir que vous essayez d'insérer la DataVendorId dans la colonne RowId qui provoque l'exception que vous ne pouvez pas convertir un varchar(32) à un int. Déplacez votre colonne d'identité à la fin de la table. Maintenant, l'insertion en bloc sera en mesure de faire correspondre tous les champs jusqu'à, il atteint la colonne d'identification.

1

J'ai eu un problème similaire où les colonnes étaient éteintes et une fois que je définissais la cartographie, aucun problème:

using (SqlBulkCopy sbc = new SqlBulkCopy(ConfigurationManager.ConnectionStrings["SQLDatabase"].ConnectionString, SqlBulkCopyOptions.KeepIdentity)) 
      { 
       sbc.DestinationTableName = "DestinationTable"; 
       sbc.ColumnMappings.Add("foo", "bar"); 
       sbc.ColumnMappings.Add("hello", "world"); 
       sbc.ColumnMappings.Add("col1", "col2"); 
       sbc.WriteToServer(data); 
      } 

Aussi, j'ai une liste à l'extension du convertisseur DataTable que j'utiliser pour convertir ma liste.

public static DataTable ToDataTable<T>(this IEnumerable<T> data) 
     { 
      PropertyDescriptorCollection properties = 
       TypeDescriptor.GetProperties(typeof(T)); 
      DataTable table = new DataTable(); 
      foreach (PropertyDescriptor prop in properties) 
       table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); 
      foreach (T item in data) 
      { 
       DataRow row = table.NewRow(); 
       foreach (PropertyDescriptor prop in properties) 
        row[prop.Name] = GetDataValue(prop.GetValue(item)); 
       table.Rows.Add(row); 
      } 
      return table; 
     } 

La méthode GetDataValue() nettoie mes données pour les dates MINVALUE, etc:

private static object GetDataValue(object value) 
     { 
      if (value == null || (value.GetType() == typeof(DateTime) && Convert.ToDateTime(value) == DateTime.MinValue) || (value.GetType() == typeof(DateTime) && Convert.ToDateTime(value) < Convert.ToDateTime("01/01/1753"))) 
      { 
       return DBNull.Value; 
      } 

      return value; 
     }