2009-07-27 20 views
17

Estoy usando SQLBulkCopy para mover grandes cantidades de datos. Implementé el evento de notificación para notificarme cada vez que se ha procesado un cierto número de filas, pero el evento OnSqlRowsCopied no se activa cuando se completa el trabajo. ¿Cómo obtengo el número total de filas copiadas cuando se completa el writetoserver de SQLBulkCopy?SQLBulkCopy Recuento de filas cuando está completo

Respuesta

3

Creo que debe ejecutar una consulta COUNT() en la tabla después de terminar, como en el ejemplo here de MSDN.

Aparte de eso, ¿no puedes decirlo al frente? p.ej. si pasa una DataTable a WriteToServer(), entonces sabrá cuántos registros hace un .Rows.Count en él.

+0

y si está utilizando una IDataReader que sólo puede envolver, nunca debería ser realmente una necesidad de llamar a la cuenta, pero es un truco que puede trabajar –

+0

@ Sam, ¿Qué quiere decir "envolverlo "¿?" Tengo un 'SqlDataReader', y lo más parecido a un recuento de filas que tiene es la propiedad' RecordsAffected' que siempre es -1 en este caso ... – chezy525

+0

Este es el método más seguro que los que se enumeran a continuación (que son, sin duda, , slick!) - acceder a un campo privado podría romperse en el futuro sin previo aviso (Microsoft podría cambiar la implementación de una API pública sin romper la API pública al cambiar los nombres de los campos), pero la consulta de recuento aún funcionaría. –

24

este truco (utilizando la reflexión) es una opción:

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

y luego usar la clase de la siguiente manera:

int rowsCopied = SqlBulkCopyHelper.GetRowsCopied(bulkCopyObjectInYourCode); 

Espero que esto ayude.

+7

¿Por qué no hacer que sea un método de extensión? public static int GetRowsCopied (este SqlBulkCopy bulkCopy) – mhenry1384

+0

Mi única preocupación aquí es que obtiene un campo interno y no juega con la API pública. Ese campo interno podría cambiar en una implementación futura sin romper la API, y eso rompería este código. (Puede que sea poco probable, pero es posible, y he visto cosas así antes). Es muy peligroso tener acceso a campos privados por esta misma razón: podría funcionar hoy, pero no hay garantía de que funcione. mañana. (Realmente, hubiera sido bueno si Microsoft acabara de exponer una propiedad pública aquí). –

4

Para completar, he implementado como un método de extensión e incluí el espacio de nombres. Copia y pega esta clase si quieres una solución rápida para obtener el recuento copiado. Nota: Este recuento no tiene en cuenta la cantidad de filas realmente insertadas cuando Ignorar duplicados está activado.

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

Mediante el uso de SqlBulkCopy.SqlRowsCopied Event (se produce cada vez que el número de filas especificado por la propiedad NotifyAfter ha sido procesado) podemos lograr SqlBulkCopy recuento de filas cuando se complete.

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

Aquí es lo que hice - es una ligera modificación de la solución de Rahul Modi en este hilo (básicamente sólo se pone la línea evento SqlRowsCopied, que creo que es un poco más limpio en este caso que la creación del nuevo controlador de eventos método):

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; 
    } 
} 
Cuestiones relacionadas