24

我正在使用 SQLBulkCopy 移动大量数据。我实现了通知事件以在每次处理了一定数量的行时通知我,但是当作业完成时 OnSqlRowsCopied 事件不会触发。当 SQLBulkCopy writetoserver 完成时,如何获取复制的总行数?

4

8 回答 8

33

以下 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);

希望这可以帮助。

于 2010-12-17T20:11:36.613 回答
8

这就是我所做的——这是对 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;
   }
}
于 2017-06-15T17:08:01.737 回答
5

为了完整起见,我已经实现了一个扩展方法并包含了命名空间。如果您想要一个快速的解决方案来获取复制的计数,请复制并粘贴此类。注意:当 Ignore Duplicates 设置为 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);
        }
    }
}
于 2012-09-04T20:37:08.157 回答
5

设置NotifyAfter为 1。在 的处理程序中SqlRowsCopied,增加一个计数器。完成WriteToServer后,读取计数器。

于 2019-01-15T15:44:42.207 回答
4

通过使用SqlBulkCopy.SqlRowsCopied Event(每次NotifyAfter处理完该属性指定的行数时发生)我们可以实现 SQLBulkCopy Row Count When 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;
}
于 2017-06-01T09:03:37.703 回答
1

我认为您必须在完成后对表运行 COUNT() 查询,如此处的 MSDN 示例中所示

除此之外,你不能提前告诉吗?例如,如果您将 DataTable 传递给 WriteToServer(),那么您可以通过对其执行 .Rows.Count 来知道有多少条记录。

于 2009-07-27T14:20:13.740 回答
0

这就是我从 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;
于 2021-09-24T04:07:49.367 回答
0

扩展方法:

(基于Benzi的回答

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(而事件 args 中的属性类型是long)。不确定如果复制 >int.MaxValue行会发生什么。

于 2019-08-07T12:11:56.890 回答