2009-07-27 12 views
17

Ich verwende SQLBulkCopy, um große Datenmengen zu verschieben. Ich habe das Benachrichtigungsereignis implementiert, um mich jedes Mal zu benachrichtigen, wenn eine bestimmte Anzahl von Zeilen verarbeitet wurde, aber das OnSqlRowsCopied-Ereignis wird nicht ausgelöst, wenn der Job abgeschlossen ist. Wie bekomme ich die Gesamtzahl der Zeilen, die beim Abschluss des SQLBulkCopy-Schreibserviceservers kopiert werden?SQLBulkCopy Zeilenanzahl nach Abschluss

Antwort

3

Ich denke, Sie müssen eine COUNT() Abfrage auf der Tabelle nach dem Beenden ausführen, wie in dem MSDN-Beispiel here.

Anders als das, können Sie nicht sagen, vorne? z.B. Wenn Sie eine DataTable an WriteToServer() übergeben, wissen Sie, wie viele Datensätze ausgeführt werden, indem Sie eine .Rows.Count ausführen.

+0

und wenn Sie eine IDataReader verwenden, können Sie es einfach wickeln, sollte es nie wirklich einen Bedarf zählen, sondern es ist ein Hack zu nennen, die –

+0

@ Sam arbeiten, Wie es Sie bedeuten „wrap "? Ich habe eine 'SqlDataReader', und die nächste Sache einer Zeilenzählung ist die' RecordsAffected' -Eigenschaft, die in diesem Fall immer -1 ist. – chezy525

+0

Dies ist die sicherere Methode als die unten aufgeführten (die zugegebenermaßen sind , glatt!) - Der Zugriff auf ein privates Feld könnte in der Zukunft ohne Warnung unterbrochen werden (Microsoft könnte die Implementierung einer öffentlichen API ändern, ohne die öffentliche API zu ändern, indem Feldnamen geändert werden), die Anzahlabfrage funktioniert jedoch weiterhin. –

24

die folgende Hack (mit Reflexion) ist eine 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); 
     } 
    } 

Und dann die Klasse wie folgt verwenden:

int rowsCopied = SqlBulkCopyHelper.GetRowsCopied(bulkCopyObjectInYourCode); 

Hoffnung, das hilft.

+7

Warum nicht eine Erweiterungsmethode? public static int GetRowsCopied (diese SqlBulkCopy-BulkCopy) – mhenry1384

+0

Meine einzige Sorge hier ist, dass es ein internes Feld bekommt und nicht mit der öffentlichen API spielt. Dieses interne Feld könnte sich in einer zukünftigen Implementierung ändern, ohne die API zu unterbrechen, und das würde diesen Code durchbrechen. (Es ist unwahrscheinlich, aber es ist möglich, und ich habe so etwas schon einmal gesehen.) Aus genau diesem Grund ist es gefährlich, auf private Felder zuzugreifen - es könnte heute funktionieren, aber es gibt keine Garantie, dass es funktioniert Morgen. (Es wäre wirklich schön gewesen, wenn Microsoft hier eine öffentliche Immobilie bloßgestellt hätte.) –

4

Der Vollständigkeit halber habe ich als eine Erweiterungsmethode implementiert und den Namespace enthalten. Kopieren und fügen Sie diese Klasse ein, wenn Sie eine schnelle Lösung benötigen, um die kopierte Anzahl zu erhalten. Hinweis: Diese Anzahl berücksichtigt nicht die Anzahl der Zeilen, die tatsächlich eingefügt werden, wenn Duplikate ignorieren auf EIN gesetzt ist.

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

Durch die Verwendung von SqlBulkCopy.SqlRowsCopied Event (Tritt jedes Mal, dass die Anzahl der Zeilen, die von der NotifyAfter Eigenschaft angegeben verarbeitet worden ist) können wir SqlBulkCopy Zeilenanzahl erreichen, wenn Sie fertig.

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

Hier ist, was ich tat - es ist eine leichte Modifikation der Lösung von Rahul Modi in diesem Thread (im Grunde ist es setzt nur das Ereignis Inline SqlRowsCopied, was meiner Meinung nach einem bisschen sauberer ist in diesem Fall als die neuen Event-Handler zu schaffen Methode):

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