2009-07-27 12 views
17

Używam SQLBulkCopy do przenoszenia dużych ilości danych. Zaimplementowałem powiadomienie, aby powiadomić mnie za każdym razem, gdy przetwarzana jest pewna liczba wierszy, ale zdarzenie OnSqlRowsCopied nie uruchamia się po zakończeniu zadania. Jak uzyskać całkowitą liczbę wierszy skopiowanych po ukończeniu skryptu skryptowego SQLBulkCopy?Liczba wierszy SQLBulkCopy po ukończeniu

Odpowiedz

3

Myślę, że po zakończeniu należy uruchomić zapytanie COUNT(), tak jak w przykładzie MSDN here.

Poza tym, nie możesz powiedzieć z przodu? na przykład jeśli przekazujesz DataTable do WriteToServer(), wtedy wiesz ile rekordów wykonujesz na .Rows.Count na tym.

+0

a jeśli używasz IDataReader można po prostu zawinąć go, nie powinno nigdy być naprawdę potrzeba, aby zadzwonić count ale jego hack, który może pracować –

+0

@Sam, jaki masz na myśli „owinąć "? Mam 'SqlDataReader', a najbliżej liczby wierszy ma właściwość' RecordsAffected', która jest zawsze -1 w tym przypadku ... – chezy525

+0

To jest bezpieczniejsza metoda niż te wymienione poniżej (które są, co prawda, , gładko!) - dostęp do prywatnego pola może przerwać się w przyszłości bez ostrzeżenia (Microsoft mógłby zmienić implementację publicznego interfejsu API bez przerywania publicznego interfejsu API poprzez zmianę nazw pól), ale zapytanie liczenia wciąż działałoby. –

24

Poniższa Hack (przy użyciu odbicia) jest opcją:

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

A następnie użyć klasy następująco:

int rowsCopied = SqlBulkCopyHelper.GetRowsCopied(bulkCopyObjectInYourCode); 

nadzieję, że to pomaga.

+7

Dlaczego nie uczynić tego metodą rozszerzenia? public static int GetRowsCopied (this SqlBulkCopy bulkCopy) – mhenry1384

+0

Moim jedynym zmartwieniem jest to, że dostaje wewnętrzne pole i nie gra z publicznym API. To wewnętrzne pole może się zmienić w przyszłej implementacji bez łamania API, a to by złamało ten kod. (Może się to wydawać mało prawdopodobne, ale jest to możliwe, i widziałem już takie rzeczy.) To właśnie jest niebezpieczny dostęp do prywatnych pól z tego właśnie powodu - może zadziałać dzisiaj, ale nie ma gwarancji, że zadziała. jutro. (Naprawdę byłoby miło, gdyby Microsoft właśnie ujawnił tutaj publiczną własność). –

4

Dla kompletności zaimplementowałem jako metodę rozszerzenia i zawarłem przestrzeń nazw. Skopiuj i wklej tę klasę, jeśli chcesz szybkiego rozwiązania, aby uzyskać skopiowaną liczbę. Uwaga: ta liczba nie uwzględnia liczby wierszy faktycznie wstawionych, gdy opcja Ignore Duplicates jest ustawiona na 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

Korzystając SqlBulkCopy.SqlRowsCopied Event (występują za każdym razem, że liczba wierszy podanych przez obiekt NotifyAfter zostały przetworzone) możemy osiągnąć SQLBulkCopy Row liczyć po zakończeniu.

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

Oto co zrobiłem - to niewielka modyfikacja rozwiązania Rahul Modi w tym wątku (w zasadzie to właśnie stawia inline zdarzeń SqlRowsCopied, co moim zdaniem jest nieco czystsze w tym przypadku niż tworzenie nowego obsługi zdarzeń metoda):

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