我有一个SQL Server存储过程负责处理大量数据,需要15秒到几分钟才能运行。我正在尝试使用来自桌面UI客户端的请求调用的异步MVC操作异步执行它。我似乎完全按照.NET异步书籍和教程的方式进行操作,但似乎存储过程在启动后不久就会中止。
启动时,存储过程会向状态表中添加一条记录,以指示正在进行的作业。成功完成后,存储过程将使用成功标志更新状态记录,并且任何错误都由TRY-CATCH块处理,该块使用错误标志更新状态记录。
这已经在SQL Server Management Studio中进行了全面测试,所以我确信存储过程不能只是悄悄地炸弹。但是,当通过异步调用执行存储过程时(见下文),作业记录将插入状态表中,但它不会更新,这意味着存储过程永远不会到达其结束并在中期终止-flight,可能是由于数据库连接关闭。
存储过程调用包含在此异步存储库方法中:
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
}
});
我在JavaScript中的两个警报以及控制器操作方法的开头都设置了断点,以确认操作是否实际执行。我在System.Diagnostics
调用存储库方法之后立即在await
行处有一个断点,以便在方法返回时捕获(尽管在调试会话中它可能毫无意义,因为线程上下文很可能是不同)。
这就是:
UpdateCatalog
操作开头的断点success
JavaScript处理程序永远不会触发System.Diagnostics
处的断点我做错了什么?
更新
事实证明,存储过程代码本身存在一个问题,即最终做了一些需要20多分钟才能完成的事情。我现在已经将它重写为快几个数量级。但是,异步的东西仍然没有像我期望的那样工作。只有控制器操作方法在success
行之后而不是立即恢复后,jQuery AJAX await
事件才会触发。
我应该如何更改我的控制器操作方法以执行真正的"“即发即忘”"方式是什么?
答案 0 :(得分:2)
await不会启动工作。它等待已经运行的工作。因此,等待并不是一种发起“一劳永逸”的手段。
更改
await _repository.ProcessCatalogUpdateAsync(uid);
到
_repository.ProcessCatalogUpdateAsync(uid);
将诊断日志移到ProcessCatalogUpdateAsync(或包装方法)中。现在这已经过去了,忘记了。
"开火,忘记"有它的危险。这项工作可能会在没有通知的情况下丢失,或者如果启动太快,并发火灾和忘记工作可能会增加并使服务过载。