Does Sitefinity support async MVC actions?
Recently, I tried to use an async action on an MVC controller in Sitefinity and was greeted with the error message:
"The asynchronous action method 'IndexAsync' returns a Task, which cannot be executed synchronously."
This was in Sitefinity 8.0. Async actions have been supported in MVC since MVC 4.0 (which is the version leveraged by Sitefinity 8.0). See Using Asynchronous Methods in ASP.NET MVC 4 for more information regarding this.
Does Sitefinity support async MVC actions? / Am I missing a setting allowing this?
If not, when will Sitefinity support this?
Thank you,
RJ Cuthbertson
For reference, I am using the following temporary workaround right now:
[AcceptVerbs(HttpVerbs.Get)]
public
ActionResult Index()
return
IndexAsync().Result;
[NonAction]
private
async Task<ActionResult> IndexAsync()...
Hey Telerik,
I'd like to know this as well.
The async creep is only going to expand further in my project as we're using an external ASP.NET Identity based solution which heavily uses the Task-based Asynchronous Pattern. Aside from that, we're using HttpClient for server-to-server web requests to communicate between various 3rd-party systems.
And I'd really like to not have to wrap every controller action with a synchronous, blocking version relying on .Result ...
Hello,
Using the controller in classic MVC will allow you to use async Actions. You can register your own route in Sitefinity 8.1 the following way:
protected
void
Application_Start(
object
sender, EventArgs e)
Bootstrapper.Initialized +=
new
EventHandler<ExecutedEventArgs>(Bootstrapper_Initialized);
protected
void
Bootstrapper_Initialized(
object
sender, ExecutedEventArgs e)
if
(e.CommandName ==
"RegisterRoutes"
)
RouteTable.Routes.MapRoute(
"Classic"
,
"customprefix/controller/action/id"
,
new
controller =
"MyController"
, action =
"Index"
, id = (
string
)
null
);
Will asynchronous actions be supported in the future in Hybrid or Pure modes?
Hi,
Unfortunately, currently this has not been discussed, so it is not in the set plans for the upcoming releases. We are now focused on improving the Feather and providing out of the box functionality for the built in web forms widgets and modules. However, it also depends on the business scenario and desirability of this feature. I have logged a feature request about this in our portal and you can review it on the bellow link:
http://feedback.telerik.com/Project/153/Feedback/Details/165662-mvc-ability-to-use-async-actions-in-mvc-widgets
Regards,
Nikola Zagorchev
Telerik
Thank you for creating that.
Hi,
Please, vote for the item and follow its status.
Thank you all.
Regards,
Nikola Zagorchev
Telerik
so given that this is still a limitation, what strategies are people using to work around it?
We have a site that is needing to make async (HttpClient) calls to an external service to load content into various Sitefinity widgets...
The way I see it we have three options:
1 - Use Webclient to make all Http calls synchronously. This seems the most straightforward, but I don't know what kind of impact it would have on performance if several hundred people are using the same page at once...
2 - Use classic MVC and handle these requests outside of the Sitefinity context. This gives us full control, but we lose all the layout and templating (not to mention other SF widgets like navigation, etc.) from sitefinity, so it's probably a last resort type option.
3 - Implement these pages using client-side requests, either via AJAX or a SPA with something like angular. I like this idea, but I don't know how you well SPA apps work on a Sitefinity page, has anybody done something like this?
Are there any other strategies for being able to work with Sitefinity MVC Feather widgets that have async tasks in their actions?
many thanks
@SelAromDotNet : You posted this in May - sorry for the late reply, I just saw this.
First, read this excellent blog post by Stephen Cleary titled "There is no Thread" - it explains that when an async method awaits a method that performs an async I/O operation, there is no thread handling the wait. In your controller action, when the thread that is processing your request within the IIS request pipeline comes to the instructions to await an async web request, it signals the machine's NIC device driver to make a request to the external service, and then the thread is released to handle processing other requests in IIS. When the request to the external service completes, the NIC driver signals (via a bunch of low level events) that the request has completed, and IIS assigns a thread pool thread to the continuation to finish the processing of your controller's action method. This means that the entire time the HTTP request to the external service is pending, the thread that was initially processing your request is free to perform other work.
So the options that you've laid out:
1) Use WebClient to make "synchronous" HTTP requests - This is effectively the same thing as blocking on an async HttpClient request. In both instances the underlying request is being performed asynchronously by the device driver, but the .NET code issuing that request is blocking the thread while waiting for a response. This means that thread is tied up, doing nothing, until the request to the external service completes.
This is what the work around I posted in the initial post is doing. Note that either of these methods synchronously execute async code, and both block the thread while doing so:
[AcceptVerbs(HttpVerbs.Get)]
public
ActionResult Index()
return
IndexAsync().Result;
// Or this does the same thing, but thrown exceptions don't get wrapped in an AggregateException...
return
Task.Run(() => IndexAsync())
.GetAwaiter()
.GetResult();
[NonAction]
private
async Task<ActionResult> IndexAsync()...
This work around, blocking on async code, can cause the execution of your request's processing to deadlock. See Don't Block on Async Code
For information about .Result vs. Task.Run(...).GetAwaiter().GetResult() see A Tour of Task, Part 6: Results
2) You could use classic MVC, but then why are you even using Sitefinity?
3) This is probably your best bet, you get to keep Sitefinity's widget functionality and you aren't blocking while synchronously executing async code. If this is possible, by all means, go for it.
You do seem to be a bit confused about what a SPA is though. Any way about it, making a web request from the client side will be an AJAX/XHR request. The Single Page Application architecture is just a way to organize your code such that there is only one HTML document requested and served during the entirety of your application's life time. The web browser loads the HTML document once, and then JavaScript is used to make AJAX/XHR requests to load content and dynamically manipulate the DOM thereafter.
Are there any other strategies for handling async actions in SF? Nope, those three are the three options you have: 1) use SF and execute async methods synchronously, blocking the thread handling the request; 2) use Classic mode which bypasses Sitefinity's routing and view engine - essentially a non-SF application with the SF backend available via the SF APIs; 3) use SF, but only non-async actions, and make AJAX requests to services hosted outside of SF that properly use async actions.
Unfortunately, none of those solve the problem of proper handling of async actions on controllers in Sitefinity. They're all just ways to work around the problem.