是否可以取消 Sql Bulk Copy.Write To Server (c#)?

Is it possible to cancel a Sql​Bulk​Copy.Write​To​Server (c#)?

我正在使用 Sql Bulk Copy.Write To Server (C#) 复制数十亿行,这需要几个小时才能完成。有时因为一些紧急的任务需要停止运行,但我不知道该怎么做。我可以在通知事件到达时做些什么吗(使用 Sql Bulk Copy.NotifyAfter 和 Sql Bulk Copy.SqlRowsCopied)?

您可以尝试修改您的 sql 查询并分块处理数据,例如,每个请求 1000 个。如果您需要停止操作,请记住块大小和数量,停止 sql 操作,执行您需要的操作,然后继续 sql 操作。 您也可以制作自己的 PaueOperationToken,例如 CancelToken。 这是 CancelToken 中的 example

Can I do something on the arriving of the notification event

作为 SQL 管理员,您总是可以找到会话 运行 批量加载和 KILL 它,就像这样:

declare @spid int = (
select session_id
from sys.dm_exec_requests
where command = 'BULK INSERT')

if @spid is not null
begin
    declare @sql varchar(200) = concat('kill ',@spid)

    exec ( @sql )

end

如果您想在代码中执行此操作,请使用接受取消令牌的 WriteToServer 的异步版本。例如

  static void Main(string[] args)
    {
        using (var src = new SqlConnection("server=.;database=tempdb;integrated security=true"))
        using (var dest = new SqlConnection("server=.;database=tempdb;integrated security=true"))
        {
            src.Open();
            dest.Open();
            var cmd = new SqlCommand("create table #t(id int)", dest);
            cmd.ExecuteNonQuery();

            bool cancel = false;
            var cancelationTokenSource = new CancellationTokenSource();
            var srcCmd = new SqlCommand("select row_number() over (order by (select null)) id from sys.objects o, sys.columns c, sys.columns c2", src);
            using (var rdr = srcCmd.ExecuteReader())
            {
                var bc = new SqlBulkCopy(dest);
                bc.NotifyAfter = 10000;
                bc.SqlRowsCopied += (s, a) =>
                {
                    Console.WriteLine($"{a.RowsCopied} rows copied");
                    if (cancel)
                    {
                        dest.Close();
                    }
                };

                bc.DestinationTableName = "#t";
                bc.ColumnMappings.Add(new SqlBulkCopyColumnMapping("id", "id"));

                var task = bc.WriteToServerAsync(rdr, cancelationTokenSource.Token);

                Console.WriteLine("Hit any key to cancel the bulk load");
                while (!task.Wait(1000))
                {
                    if (Console.KeyAvailable)
                    {

                        cancelationTokenSource.Cancel();
                        try
                        {
                            task.Wait();
                        }
                        catch (AggregateException ex)
                        {
                            Console.WriteLine(ex.InnerException.Message);
                            Console.WriteLine("WriteToServer Canceled");
                            break;
                        }
                    }
                }

                Console.WriteLine("Hit any key to exit");
                return;
            }




        }
    }

MSDN documentation 说:

If the user wants to cancel the operation from the event, the Abort property of the SqlRowsCopiedEventArgs can be used.

所以下面的代码应该可以解决问题:

    SqlBulkCopy cp = new SqlBulkCopy("your connection string");
    cp.NotifyAfter = 1000; // use here whatever number floats your boat
    cp.SqlRowsCopied += (sender, eventArgs) => eventArgs.Abort = true; // add some more conditions here