如何更改异步方法调用以防止强制异步调用堆栈

时间:2014-03-30 03:09:30

标签: c# asp.net-mvc asynchronous task-parallel-library servicebus

如果我需要调用一个方法,在内部调用一些异步方法,作为一个火灾和忘记操作,我怎么能阻止这个调用强制“异步”需要用完调用堆栈来说。 .. MVC控制器?

例如:我的MVC Controller(非异步)调用业务层方法,该方法又调用Windows Azure Service Bus QueueClient.SendAsync(BrokeredMessage),将消息放入队列中,但不需要等待它完成。

通常,当调用此控制器操作时,编译器将抛出一个错误,指出此时无法启动异步操作。

我知道不是等待或只是调用SendAsync()方法,我可以使用ContinueWith()跟进它,以便在异步操作的回调上执行代码,但我被告知这不是正确解决方案(参见对Calling async method in controller的回复)

有人会关心如何修复此方案的最佳方法吗?并告诉我为什么ContinueWith()方法不正确?

2 个答案:

答案 0 :(得分:2)

MVC控制器方法处理HTTP请求并将相应的HTTP响应发送回客户端。如果我正确地理解了你的问题,你想从你的异步MVC控制器方法中调用一个fire-and-forget方法,然后继续进行HTTP响应传递,因此即发即弃方法不会保持响应。

实际上,您无法通过这种方式使用控制器方法触发它,而无需等待其结果。即,您可以,但是如果您的ASP.NET应用程序将重新启动,或者IIS服务器从服务器场中取出,则永远不会调用ContinueWith回调。所以,你不知道请求是否曾经到过Windows Azure服务。

解决此问题的一种方法是在同一主机上或同一网络上的另一台主机上运行帮助程序Web API或WCF服务(因此服务调用的转换非常快)。它可以是自托管服务。您可以从MVC控制器调用此帮助程序服务,只需排队即时取消操作即可。您await此调用的结果,但在这种情况下它不是问题,因为此操作的调用者和被调用者都将存在于同一网络中。

这样,原始的MVC HTTP响应就不会被搁置。在帮助程序服务中,您然后执行await QueueClient.SendAsync()来调用Windows Azure总线并相应地处理此操作的结果。

答案 1 :(得分:2)

  

调用Windows Azure Service Bus QueueClient.SendAsync(BrokeredMessage),将消息放入队列中,但不需要等待它完成。

首先,我重新考虑这个假设。如果您想要一个完全可靠的系统,操作应该等待将消息发送到总线。请注意,这通常是一个快速操作(100毫秒)。

  

通常,当调用此控制器操作时,编译器将抛出一个错误,指出此时无法启动异步操作。

确保您没有任何async void方法或EAP method calls。如果Azure存储库导致该异常,我会非常惊讶。

  

有人会关心如何修复此方案的最佳方法吗?并告诉我为什么ContinueWith()方法不正确?

最好的解决方案是拥抱asyncContinueWith可以工作但使用起来很危险(它有很多参数,其中一些参数有不安全的默认值); await几乎与ContinueWith相同,但没有危险的默认值。


但是,如果100ms确实无法忍受,并且您愿意放弃可靠性(在这种情况下,这意味着您接受某些消息可能无法发送到总线的事实,即使操作成功完成,因此客户认为他们已经),然后您可以使用BackgroundTaskManager from my blog来尽量减少丢失邮件的可能性。