Sto utilizzando SQLBulkCopy per spostare grandi quantità di dati. Ho implementato l'evento di notifica per notificarmi ogni volta che è stato elaborato un determinato numero di righe, ma l'evento OnSqlRowsCopied non viene attivato al termine del processo. Come si ottiene il numero totale di righe copiate quando SQLetuperver SQLBulkCopy è completato?Conteggio righe SQLBulkCopy al termine
risposta
Penso che sia necessario eseguire una query COUNT() sul tavolo dopo aver terminato, come nell'esempio MSDN here.
A parte questo, non puoi dirlo in anticipo? per esempio. se stai passando un DataTable a WriteToServer(), sai quanti record esegui .Rows.Count su di esso.
Di seguito mod (utilizzando la riflessione) è un'opzione:
/// <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);
}
}
e quindi utilizzare la classe come segue:
int rowsCopied = SqlBulkCopyHelper.GetRowsCopied(bulkCopyObjectInYourCode);
Spero che questo aiuti.
Perché non renderlo un metodo di estensione? static static int GetRowsCopied (questo SqlBulkCopy bulkCopy) – mhenry1384
La mia unica preoccupazione è che ottiene un campo interno e non gioca con l'API pubblica. Quel campo interno potrebbe cambiare in un'implementazione futura senza rompere l'API e questo romperebbe questo codice. (Può essere improbabile, ma è possibile, e ho visto cose del genere accadere prima.) È piuttosto pericoloso accedere ai campi privati proprio per questo motivo - potrebbe funzionare oggi, ma non è garantito che funzioni Domani. (Davvero, sarebbe stato bello se Microsoft avesse appena esposto una proprietà pubblica qui). –
Per completezza ho implementato come metodo di estensione e incluso lo spazio dei nomi. Copia e incolla questa classe se vuoi una soluzione veloce per ottenere il conteggio copiato. Nota: questo conteggio non tiene conto del numero di righe effettivamente inserite quando Ignora duplicati è impostato su 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);
}
}
}
Utilizzando SqlBulkCopy.SqlRowsCopied Event
(si verifica ogni volta che il numero di righe specificato dalla proprietà NotifyAfter
è stato elaborato) siamo in grado di raggiungere il conte SqlBulkCopy riga quando completa.
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;
}
Ecco quello che ho fatto - è una leggera modifica della soluzione di Rahul Modi in questa discussione (in fondo si mette solo la linea evento SqlRowsCopied, che credo sia un po 'più pulita in questo caso che la creazione del nuovo gestore di eventi metodo):
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;
}
}
e se si utilizza un IDataReader si può semplicemente avvolgere, non ci dovrebbe mai essere una necessità di chiamare conteggio, ma è un hack che può funzionare –
@ Sam, che senso "avvolgerlo "? Ho un 'SqlDataReader', e la cosa più vicina a un conteggio di riga che ha è la proprietà' RecordsAffected' che è sempre -1 in questo caso ... – chezy525
Questo è il metodo più sicuro di quelli elencati di seguito (che sono, certamente , slick!) - l'accesso a un campo privato potrebbe interrompersi in futuro senza preavviso (Microsoft potrebbe modificare l'implementazione di un'API pubblica senza rompere l'API pubblica cambiando i nomi dei campi), ma la query di conteggio funzionerebbe ancora. –