Мне нужно подсчитать последовательные исключения таймаута из 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;