SQLBulkCopyを使用して大量のデータを移動しています。特定の行数が処理されるたびに通知するように通知イベントを実装しましたが、ジョブの完了時にOnSqlRowsCopiedイベントが発生しません。SQLBulkCopy writetoserverの完了時にコピーされた行の総数を取得するにはどうすればよいですか?
8 に答える
次のハック(リフレクションを使用)はオプションです:
/// <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);
お役に立てれば。
これが私がしたことです-これはこのスレッドのRahulModiのソリューションのわずかな変更です(基本的には、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;
}
}
完全を期すために、拡張メソッドとして実装し、名前空間を含めました。コピーされたカウントを取得するための高速なソリューションが必要な場合は、このクラスをコピーして貼り付けてください。注:このカウントでは、[重複を無視]が[オン]に設定されている場合に実際に挿入される行数は考慮されません。
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);
}
}
}
1に設定NotifyAfter
します。のハンドラーでSqlRowsCopied
、カウンターをインクリメントします。完了しWriteToServer
たら、カウンターを読みます。
SqlBulkCopy.SqlRowsCopied Event
(プロパティで指定された行数が処理されるたびに発生します)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;
}
ここのMSDNの例のように、終了後にテーブルに対してCOUNT()クエリを実行する必要があると思います。
それ以外は、前もって言えませんか?たとえば、DataTableをWriteToServer()に渡す場合は、.Rows.Countを実行することでレコード数がわかります。
拡張メソッド:
( 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);
}
}
.NET4.6.1の時点でテスト済みおよび動作
フィールドのタイプはint
(イベントargsのプロパティのタイプはlong
)であることに注意してください。int.MaxValue
>行をコピーするとどうなるかわかりません。
これは、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;