我正在使用SQLBulkCopy移动大量数据。我已经实现了通知事件来在处理特定数量的行时通知我,但是当作业完成时,OnSqlRowsCopied事件不会触发。在SQLBulkCopy writetoserver完成后,如何获得复制的总行数?
我正在使用SQLBulkCopy移动大量数据。我已经实现了通知事件来在处理特定数量的行时通知我,但是当作业完成时,OnSqlRowsCopied事件不会触发。在SQLBulkCopy writetoserver完成后,如何获得复制的总行数?
以下 hack(使用反射)是一种选择:
/// <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);
}
}
然后按以下方式使用该类:
int rowsCopied = SqlBulkCopyHelper.GetRowsCopied(bulkCopyObjectInYourCode);
希望这可以帮助你。以下是我所做的——这是对Rahul Modi在本线程中提供的解决方案的轻微修改(基本上只是把SqlRowsCopied事件放到了同一行,我认为在这种情况下这样做比创建新的事件处理方法要更简洁一些):
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;
}
}
将NotifyAfter
设置为1。在 SqlRowsCopied
的处理程序中递增计数器。在完成 WriteToServer
后,读取计数器。
为了完整性,我已经实现了一个扩展方法并包含了命名空间。如果你想要一个快速的解决方案来获取复制计数,请复制并粘贴这个类。注意:当设置“忽略重复项”时,此计数不考虑实际插入的行数。
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);
}
}
}
通过使用SqlBulkCopy.SqlRowsCopied事件
(在处理由NotifyAfter
属性指定的行数之后每次发生)我们可以实现SQLBulkCopy完成后的行数统计。
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;
}
这是我从 SqlBulkCopy 中获取行数的方法,重要的是你需要将 NotifyAfter 设置为 1:
var rowsInserted = 0L;
using var sbc = new SqlBulkCopy(dbConnection, SqlBulkCopyOptions.KeepIdentity, transaction);
sbc.NotifyAfter = 1;
sbc.SqlRowsCopied += (s, e) => rowsInserted = e.RowsCopied;
sbc.WriteToServer(dr);
//Get row count
return rowsInserted;
using System;
using System.Reflection;
using System.Data.SqlClient;
using static System.Reflection.BindingFlags;
namespace Extensions
{
public static class SqlBulkCopyExtensions
{
private static readonly Lazy<FieldInfo> _rowsCopiedLazy = new Lazy<FieldInfo>(()
=> typeof(SqlBulkCopy).GetField("_rowsCopied", NonPublic | GetField | Instance));
public static int GetRowsCopied(this SqlBulkCopy sqlBulkCopy)
=> (int)_rowsCopiedLazy.Value.GetValue(sqlBulkCopy);
}
}
经测试,适用于.NET 4.6.1
请注意,字段类型为int
(而事件参数中属性的类型为long
)。如果复制了超过int.MaxValue
行,则不确定会发生什么。
sqlBulkCopyObject.GetRowsCopied()
,这个方法非常好用! - TechSpud我认为在完成后,您需要在表上运行COUNT()查询,就像MSDN示例这里中所示。
除此之外,您不能事先告诉吗?例如,如果您将DataTable传递给WriteToServer(),则可以通过对其执行.Rows.Count来知道有多少条记录。
SqlDataReader
,最接近行数的属性是RecordsAffected
,但在这种情况下它总是-1... - chezy525