У меня есть хранимая процедура SQL Server, ответственная за обработку большого количества данных, которые занимают от 15 секунд до нескольких минут для запуска. Я пытаюсь выполнить его асинхронно, используя действие async MVC, вызванное запросом от клиента пользовательского интерфейса. Кажется, я все делаю так, как учит .NET-асинхронные книги и учебники, но, похоже, хранимая процедура прерывается вскоре после старта.Асинхронное выполнение долговременной хранимой процедуры через webservice
После запуска хранимая процедура добавляет запись в таблицу состояния для указания текущей работы. После успешного завершения хранимая процедура обновляет запись состояния с помощью флага успеха, а любые ошибки обрабатываются блоком TRY-CATCH, который обновляет запись состояния с помощью флага ошибки.
Это было тщательно протестировано в SQL Server Management Studio, поэтому я уверен, что хранимая процедура не может просто бомбить спокойно. Однако, когда хранимая процедура выполняется через асинхронный вызов (см. Ниже), запись о задании вставляется в таблицу состояния, но она не обновляется, что означает, что хранимая процедура никогда не достигает своего конца и прекращается в середине полета , возможно, из-за закрытого соединения с базой данных.
Хранимая процедура вызова заворачивают в этом ASync метод репозитория:
public async Task ProcessCatalogUpdateAsync(Guid updateID)
{
try
{
using (var cmd = new SqlCommand("ProcessCatalogUpdate", new SqlConnection(_connectionString)))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 0;
cmd.Parameters.Add("@UpdateID", SqlDbType.UniqueIdentifier).Value = updateID;
cmd.Connection.Open();
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
}
}
catch (Exception ex)
{
var message = String.Format("Error processing catalog update ID={0}", updateID);
throw new DataException(message, ex);
}
}
Действие контроллер выглядит следующим образом:
public async Task UpdateCatalog(string updateID)
{
var uid = Guid.Empty; // breakpoint here
if (!Guid.TryParse(updateID, out uid))
throw new Exception("Bad UpdateID in request!");
await _repository.ProcessCatalogUpdateAsync(uid);
System.Diagnostics.Trace.WriteLine("Catalog update completed"); // breakpoint here
}
Я построил тестовую в виде просто HTML, которая асинхронно вызывает действие с использованием jQuery, например:
var requestURL = "/catalog/updateCatalog?updateID=" + $("#updateID").val();
$.ajax({
url: requestURL,
type: "GET",
error: function (xhr, status, errorText) {
alert("Error: " + errorText); // breakpoint here
},
success: function() {
alert("Call returned!"); // breakpoint here
}
});
Я положил br eakpoints в обоих предупреждениях в JavaScript, а также в начале моего метода действий контроллера, чтобы подтвердить, что действие действительно выполнено. У меня также есть точка останова на линии System.Diagnostics
сразу после вызова метода репозитория await
, когда метод возвращается (хотя в отладочном сеансе может быть бессмысленным, поскольку контекст потока, вероятно, будет отличаться).
Это то, что происходит:
- точку останова в начале
UpdateCatalog
действия ударил - Хранимая процедура вставляет новую строку в таблицу состояния
success
обработчик JavaScript никогда не срабатывает- Таблица состояния никогда не обновляется ни с символом успеха, ни с ошибкой
- Точка останова на
System.Diagnostics
никогда не будет h его
Что я делаю неправильно?
UPDATE
Как оказалось, была проблема в хранимой процедуре самого кода, который в самом конце было что-то делает, что принимает более 20 минут. Я теперь переписал его, чтобы работать на несколько порядков быстрее. Тем не менее, асинхронный материал по-прежнему работает не так, как я ожидаю.Событие jQuery AJAX success
срабатывает только после того, как метод действия контроллера возобновляется после строки await
, а не сразу.
Как изменить способ действия контроллера для выполнения в режиме «огонь и забухание»?
", который занимает от 15 секунд до нескольких минут для запуска" - время по умолчанию для SqlConnection составляет 15 секунд. Вы 1) подтвердили, что соединение было обновлено с превышением времени ожидания, превышающим ожидаемое время выполнения? 2) Профилировали ли вы SQL Server, чтобы проверить, выполняется ли запрос как ожидалось? –
'ConnectionTimeout' не имеет никакого отношения к тому, как долго выполняется команда, это' CommandTimeout'. – Crowcoder
@MetroSmurf, я установил CommandTimeout в ноль (неограниченно). И, как я упоминал в своем сообщении, хранимый процесс начинает исполняться (я знаю это из-за наличия новой строки в таблице состояния), но никогда не заканчивается. –