为控制器操作设置超时

我已经遇到过这个post ,但我可能还需要其他一些东西来解决我的问题。

我有一个返回ViewResult的动作,该动作由客户端的$.post()调用

JavaScript的:

 var link = 'GetFoo?fooBar=' + fooBar; var jqxhr = $.post(link, function (response) { $('#myDiv').replaceWith(response); }); 

控制器:

 public ViewResult GetFoo(String fooBar) { if (Request.IsAjaxRequest()) { // perform a ridiculously long task (~12 minutes) // algorithm: 1) download files from the Azure blob storage // 2) update each file // 3) reupload to blob storage // 4) return a list of URIs to be displayed to the UI return View("MyFooView", data); } throw new InvalidOperationException(); } 

正如评论所暗示的那样,Controller内部有一个长任务。 (这是一个文档生成模块,可将PDF上载到Azure blob存储,并将链接返回到View。)

这在我的开发机器中运行良好,但是当它在(安全)Azure生产环境中运行时,它会超时 。 我已经在各处输入了大量的日志记录条目,事实certificate,它能够上传文档并返回控制器(即它到达上面的控制器返回语句)。 但是,当需要将模型数据返回到View时,客户端脚本不会回调(即div内容不会被结果替换)。

有没有办法以某种方式延长通话的超时? 在我的(不安全的)本地环境中很难重现,因此明确的修复将有所帮助。

如果我在GetFoo()方法上使用[AsyncTimeout(3600)]属性,则永远不会从UI调用此操作。

任何建议将不胜感激。

问题是Azure负载均衡器有自己的超时设置为一分钟。 任何超过一分钟的请求都会被终止 。 没有办法改变这一点。

在Azure环境中解决这个问题的方法是让一个ajax调用启动进程并返回某种进程ID,然后让客户端轮询另一个ajax调用以传入此进程ID以查看它是否完整。 它可能看起来像这个未编译和未经测试的代码。 在javascript中:

 var link = 'BeginFooProcessing?fooBar=' + fooBar; var jqxhr = $.post(link, function (response) { var finishedlink = 'CheckFooFinished?fooId=' + response; // Check to see if we're finished in 1 second setTimeout("CheckIfFinishedYet('" + finishedlink + "')", 1000); }); function CheckIfFinishedYet(finishedlink) { var response = $.post(finishedlink, function (response) { if (response == null) { // if we didn't get a result, then check in another second setTimeout("CheckIfFinishedYet('" + finishedlink + "')", 1000); } else { // Yay, we've got a result so we'll just write it out $('#myDiv').replaceWith(response); } }); } 

在你的控制器中:

 public ViewResult BeginFooProcessing(String fooBar) { if (Request.IsAjaxRequest()) { Guid fooId = Guid.NewGuid(); var result = new FooResult { FooId = fooId, HasFinishedProcessing = false, Uris = new List() }; // This needs to go to persistent storage somewhere // as subsequent requests may not come back to this // webserver result.SaveToADatabaseSomewhere(); System.Threading.Tasks.Task.Factory.StartNew(() => ProcessFoo(fooId)); return View("MyFooStartView", fooId); } throw new InvalidOperationException(); } private void ProcessFoo(Guid fooId) { // Perform your long running task here FooResult result = GetFooResultFromDataBase(fooId); result.HasFinishedProcessing = true; result.Uris = uriListThatWasCalculatedAbove; result.SaveToADatabaseSomewhere(); } public ViewResult CheckFooFinished(Guid fooId) { if (Request.IsAjaxRequest()) { FooResult result = GetFooResultFromDataBase(fooId); if (result.HasFinishedProcessing) { // Clean up after ourselves result.DeleteFromDatabase(); return View("MyFooFinishedView", result.Uris); } return View("MyFooFinishedView", null); } throw new InvalidOperationException(); } private class FooResult { public Guid FooId { get; set; } public bool HasFinishedProcessing { get; set; } public List Uris; } 

希望这会给你一个起点。

你想看看这个

回答你的问题是:[AsyncTimeout(3600000)]

在这里看到更多控制器超时MVC 3.0

要使用Async控制器,您的控制器必须从AsyncControllerinheritance:

 public class WebsiteController : AsyncController 

然后使用异步方法的任何Action都必须使用格式

 public void ActionNameAsync(int param) public ActionResult ActionNameCompleted(int param) 

其中ActionName是您的操作的名称,而不是Async函数使用

 AsyncManager.OutstandingOperations.Increment(); 

每次你开始一个新的aysnchronous方法和

 AsyncManager.OutstandingOperations.Decrement(); 

当方法完成后,在所有未完成的操作完成后,它将继续移动到Completed函数(确保在async函数中指定完成函数所需的参数,以便它知道要传递的内容)

 AsyncManager.Parameters["param"] = "my name"; 

然后使用AsyncTimeout属性实际上会影响该函数。 如果您尝试将该属性应用于不在异步控制器中的操作,我不确定会发生什么。

这些更改不需要更改javascript中对该操作的任何引用,而不是因为您仍然只是请求’ActionName’,它会查看是否存在Async / Completed安装版本,如果没有,则d寻找正常的动作,并使用它找到的任何一个。