2009-07-27 5 views
17

J'utilise SQLBulkCopy pour déplacer de grandes quantités de données. J'ai implémenté l'événement de notification pour me notifier chaque fois qu'un certain nombre de lignes ont été traitées, mais l'événement OnSqlRowsCopied ne se déclenche pas lorsque le travail est terminé. Comment puis-je obtenir le nombre total de lignes copiées lorsque le writetoserver SQLBulkCopy est terminé?Nombre de lignes SQLBulkCopy une fois rempli

Répondre

3

Je pense que vous devez exécuter une requête COUNT() sur la table après avoir terminé, comme dans l'exemple MSDN here.

À part ça, vous ne pouvez pas dire au premier plan? par exemple. Si vous passez un DataTable à WriteToServer() alors vous savez combien d'enregistrements en faisant un .Rows.Count dessus.

+0

et si vous utilisez un IDataReader vous pouvez simplement envelopper, il ne devrait jamais être vraiment nécessaire d'appeler son décompte, mais un hack qui peut fonctionner –

+0

@ Sam, comment voulez-vous dire « envelopper "? J'ai un 'SqlDataReader', et la chose la plus proche d'un compte de lignes est la propriété' RecordsAffected' qui est toujours -1 dans ce cas ... – chezy525

+0

Ceci est la méthode la plus sûre que celles listées ci-dessous (qui sont, , lisse!) - accéder à un champ privé pourrait se casser dans le futur sans avertissement (Microsoft pourrait changer l'implémentation d'une API publique sans casser l'API publique en changeant les noms de champs), mais la requête de compte fonctionnerait toujours. –

24

Le hack (utilisant la réflexion) est une option:

/// <summary> 
    /// Helper class to process the SqlBulkCopy class 
    /// </summary> 
    static class SqlBulkCopyHelper 
    { 
     static FieldInfo rowsCopiedField = null; 

     /// <summary> 
     /// Gets the rows copied from the specified SqlBulkCopy object 
     /// </summary> 
     /// <param name="bulkCopy">The bulk copy.</param> 
     /// <returns></returns> 
     public static int GetRowsCopied(SqlBulkCopy bulkCopy) 
     { 
      if (rowsCopiedField == null) 
      { 
       rowsCopiedField = typeof(SqlBulkCopy).GetField("_rowsCopied", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance); 
      } 

      return (int)rowsCopiedField.GetValue(bulkCopy); 
     } 
    } 

Et puis utilisez la classe comme suit:

int rowsCopied = SqlBulkCopyHelper.GetRowsCopied(bulkCopyObjectInYourCode); 

Hope this helps.

+7

Pourquoi ne pas en faire une méthode d'extension? public static int GetRowsCopied (ce bulkCopy SqlBulkCopy) – mhenry1384

+0

Ma seule préoccupation ici est qu'il obtient un champ interne et ne joue pas avec l'API publique. Ce champ interne pourrait changer dans une future implémentation sans casser l'API, et cela casserait ce code. (C'est peut-être improbable, mais c'est possible, et j'ai vu des choses comme ça avant.) Il est dangereux d'accéder à des champs privés pour cette raison - cela pourrait fonctionner aujourd'hui, mais il n'y a aucune garantie que cela fonctionne demain. (Vraiment, ça aurait été bien si Microsoft venait d'exposer une propriété publique ici.) –

4

Pour être complet, j'ai implémenté comme méthode d'extension et inclus l'espace de noms. Copiez et collez cette classe si vous voulez une solution rapide pour obtenir le nombre copié. Remarque: Ce nombre ne prend pas en compte le nombre de lignes réellement insérées lorsque Ignorer les doublons est défini sur ON.

namespace System.Data.SqlClient 
{  
    using Reflection; 

    public static class SqlBulkCopyExtension 
    { 
     const String _rowsCopiedFieldName = "_rowsCopied"; 
     static FieldInfo _rowsCopiedField = null; 

     public static int RowsCopiedCount(this SqlBulkCopy bulkCopy) 
     { 
      if (_rowsCopiedField == null) _rowsCopiedField = typeof(SqlBulkCopy).GetField(_rowsCopiedFieldName, BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance);    
      return (int)_rowsCopiedField.GetValue(bulkCopy); 
     } 
    } 
} 
5

En utilisant SqlBulkCopy.SqlRowsCopied Event (produit chaque fois que le nombre de lignes spécifié par la propriété NotifyAfter a été traitée), nous pouvons obtenir SqlBulkCopy Row Count une fois terminé.

using (SqlBulkCopy s = new SqlBulkCopy(db.Database.Connection as SqlConnection)) 
{ 
    s.SqlRowsCopied += new SqlRowsCopiedEventHandler(sqlBulk_SqlRowsCopied); 
    s.BatchSize = csvFileData.Rows.Count;//DataTable 
    s.NotifyAfter = csvFileData.Rows.Count; 
    foreach (var column in csvFileData.Columns) 
    s.ColumnMappings.Add(column.ToString(), column.ToString()); 
    // Set the timeout. 
    s.BulkCopyTimeout = 60; 
    s.DestinationTableName = "Employee_Data"; 
    s.WriteToServer(csvFileData); 
} 

private static void sqlBulk_SqlRowsCopied(object sender, SqlRowsCopiedEventArgs e) 
{ 
    long Count = e.RowsCopied; 
} 
2

Voici ce que je l'ai fait - c'est une légère modification de la solution de Rahul Modi dans ce fil (fondamentalement, il met juste la ligne d'événement SqlRowsCopied, qui je pense est un peu plus propre dans ce cas que la création du nouveau gestionnaire d'événements méthode):

private long InsetData(DataTable dataTable, SqlConnection connection) 
{ 
    using (SqlBulkCopy copier = new SqlBulkCopy(connection)) 
    { 
     var filesInserted = 0L; 

     connection.Open(); 

     copier.DestinationTableName = "dbo.MyTable"; 
     copier.NotifyAfter = dataTable.Rows.Count; 
     copier.SqlRowsCopied += (s, e) => filesInserted = e.RowsCopied; 
     copier.WriteToServer(dataTable); 

     connection.Close(); 

     return filesInserted; 
    } 
} 
Questions connexes