Я должен считать последовательные исключения тайм-аута из SqlBulkCopy. Для тестирования этого я использую внешнее приложение, чтобы запустить транзакцию и запереть целевую таблицу.
Только на первом вызове делает SqlBulkCopy, выдают исключение тайм-аута при ожидании. Мы попытались использовать внешнее соединение и транзакцию, а также использовать строку подключения и внутреннюю транзакцию. С внешним соединением и транзакцией, бесконечное ожидание никогда не было в открытии соединения или начало или фиксация транзакции, но всегда в .WriteToServer()
.
Есть ли некоторый подход к этому посредством чего SqlBulkCopy.WriteToServer()
надежно выдаст исключение тайм-аута, когда оно достигнет .BulkCopyTimeout
предел?
public void BulkCopy(string connectionString, DataTable table, int bulkTimeout)
{
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(
connectionString,
SqlBulkCopyOptions.UseInternalTransaction))
{
bulkCopy.BulkCopyTimeout = bulkTimeout;//e.g. 120 sec.
//... fill with data, map columns...
bulkCopy.WriteToServer(table);
// ^^^^ waits indefinitely, doesn't throw until *after*
// the lock is released.
}
}
Я предпочитаю позволять пузырю исключений, а не обрабатывать их в пределах using
блок, но я могу всегда повторно бросать. Большое спасибо за любое понимание.
Обновление 1:
Все еще никакое разрешение. Интересное поведение обнаружило, хотя - нормальный SqlCommand бросит TimeoutException как ожидалось во время той же блокировки, которая делает SqlBulkCopy. Метод WriteToServer зависает неограниченно долго.
Вот подходы, которые мы попробовали - и которым не удалось - получить SqlBulkCopy. WriteToServer для последовательного броска тайм-аутов при ожидании:
На данный момент, как обходное решение, я чередуюсь между a) помещением WriteToServer звонят в асинхронную обертку, таким образом, я могу время это сам и b) только вызовом WriteToServer однажды; после тайм-аутов ожидайте, пока обычный SqlCommand не успешно выполняется прежде, чем попробовать WriteToServer снова. Используя эти подходы, я, по крайней мере, могу остаться в управлении потока выполнения.
Пробовали ли вы передать параметр SqlBulkOptions.TableLock в SqlBulkCopy? Эта опция (цитата) означает, что она:
Получит блокировку массового обновления на продолжительность операции массового копирования.
Таким образом, если есть другая обработка, блокирующая таблицу, это предотвратит получение блокировки и, теоретически, надежно приведет к тайм-ауту.
Обновление:
Я установил свою испытательную привязь и не могу воспроизвести. Чтобы заблокировать таблицу, я начал транзакцию в SSMS, выполнив SELECT * FROM TargetTable WITH (HOLDLOCK)
. Я использовал тот же метод BulkCopy, который вы указали в вопросе, с использованием внутренних транзакций с таймаутом массовой загрузки 30 секунд. Каждая попытка массового копирования истекает, как и ожидалось, через 30 секунд. Затем он завершается успешно, когда я откатываю транзакцию SSMS.
Я использовал SQL Server 2008 Express, .NET 3.5.
Это не похоже на то, что после первой попытки тайм-аут массовой загрузки не передается правильно? т.е. он каким-то образом не установлен на "неопределенный".
Обновление 2:
Также включена поддержка нескольких активных наборов результатов в строке подключения, но каждый раз время ожидания постоянно истекает.
У меня была последняя проблема и для решения этой проблемы установите BulkCopyTimeout равным нулю.
bulkCopy.BulkCopyTimeout = 0;