Does Sitefinity support async MVC actions?

Posted by Community Admin on 03-Aug-2018 16:38

Does Sitefinity support async MVC actions?

All Replies

Posted by Community Admin on 27-Jul-2015 00:00

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()...

Posted by Community Admin on 30-Jul-2015 00:00

Hey Telerik,

 I'd like to know this as well. 

Posted by Community Admin on 30-Jul-2015 00:00

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 ...

Posted by Community Admin on 30-Jul-2015 00:00

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
                );
            
        

This way you can use the async controller actions freely. They will be handled as in a normal web application.
However, if you use the async Action in a widget, you will indeed get the InvalidOperationException. Currently, we do not support rendering and using MVC Widgets async Actions on both hybrid and pure pages. As you can see from the StackTrace of the error:
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
The ControllerActionInvoker is used rather than the AsyncControllerActionInvoker. You can also observe from the external code while in debugging in VS that the async invoker is called when serving the Action under classic mode when it works correctly.

Hope this info is useful.

Regards,
Nikola Zagorchev
Telerik
 
Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Sitefinity CMS Ideas&Feedback Portal and vote to affect the priority of the items
 

Posted by Community Admin on 30-Jul-2015 00:00

Will asynchronous actions be supported in the future in Hybrid or Pure modes?

Posted by Community Admin on 04-Aug-2015 00:00

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

 
Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Sitefinity CMS Ideas&Feedback Portal and vote to affect the priority of the items
 

Posted by Community Admin on 04-Aug-2015 00:00

Thank you for creating that.

Posted by Community Admin on 07-Aug-2015 00:00

Hi,

Please, vote for the item and follow its status.
Thank you all.

Regards,
Nikola Zagorchev
Telerik

 
Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Sitefinity CMS Ideas&Feedback Portal and vote to affect the priority of the items
 

Posted by Community Admin on 09-May-2017 00:00

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

Posted by Community Admin on 06-Aug-2017 00:00

@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.

This thread is closed