Using the BeginMethod/EndMethod pattern with MVC
If your asynchronous action method calls a service which exposes methods using the
BeginMethod() / EndMethod() pattern , your callback will be executed on a thread which is not under the
control of ASP.NET. Some consequences of this are that HttpContext.Current will be null, and
there will be race conditions accessing members like AsyncManager.Parameters. To restore
HttpContext.Current and eliminate the race condition, call AsyncManager.Sync() from within
your callback.
public void NewsAsync(string city) {
AsyncManager.OutstandingOperations.Increment();
NewsService newsService = new NewsService();
newsService.BeginGetNews(city, ar =>
{
AsyncManager.Sync(() =>
{
AsyncManager.Parameters["news"] =
newsService.EndGetNews(ar);
AsyncManager.OutstandingOperations.Decrement();
});
}, null);
}
Alternatively, you can use the AsyncManager.RegisterTask() extension method from MVC
Futures, which handles synchronization on your behalf. It also handles incrementing and
decrementing the OutstandingOperations counter so that you don’t have to.
public void NewsAsync(string city) {
NewsService newsService = new NewsService();
AsyncManager.RegisterTask(
callback =>
newsService.BeginGetNews(city, callback, null),
ar =>
{
AsyncManager.Parameters["news"] =
newsService.EndGetNews(ar);
}
);
}
Be sure to reference the Futures assembly and import the Microsoft.Web.Mvc namespace if
you want to use this extension method.
The following is condensed from https://forums.asp.net/t/1499832.aspx
Of three async patterns existing within futures assembly (begin/end, event, delegate) only one is present (slightly modified event pattern) in MVC 2.
Levi responds:
There are currently no plans to reintroduce the other two patterns before MVC 2 releases. This decision is driven by the fact that most of the feedback we've received is in support of the event pattern rather than the delegate or APM patterns, so we've focused our limited resources on implementing and supporting that pattern. It also encourages testability of actions, which is important to many of the MVC developers from whom we've received comments.
If you want to support the delegate or APM patterns in your own application, you'll have to subclass the invoker. Specifically, override the AsyncControllerActionInvoker.GetControllerDescriptor() or FindAction() methods. Then that action descriptor can use your custom implementation of BeginExecute() / EndExecute(). This will involve some duplication of our internal code, but it shouldn't be too bad.
We're currently looking at ways of making our extensibility points easier to use and require less code or less duplication. This would be broad across MVC and not necessarily confined to the invoker. But this is a longer-term goal and isn't something we're likely to get to before MVC 2 ships.
Comments
- Anonymous
October 01, 2012
As per MSDN "msdn.microsoft.com/.../ee728598(v=vs.98).aspx", calling Sync method from a thread that is already under the control of ASP.NET has undefined behavior. Therefore, don't we need to check whether CompletedSynchronously is false before calling Sync method.