Pregunta

I have an Action (Controller Action)that can take a couple minutes to run under some circumstances. I would like to provide our users a visual indication that the Action is running, such as a message, a spinner, or a progress bar. The trouble is, I'm having a tough time figuring out how to "detect" on the front end that the Action has completed.

After researching this for a bit, I stumbled across an article titled "Using Asynchronous Methods in ASP.NET MVC 4." It explains the use of asynchronous actions and .NET Tasks. It looks like good stuff, and I'm going to spend some time studying it, but I am too new to the concept to really understand if it is going to give me what I want.

So, my question to the SO community is, if you have lots of experience with Asynch Actions and Tasks, are these going to help me provide visual feedback to my users while my Action executes? Or, am I starting down the wrong trail?

EDIT:

Thanks, everyone, for your responses. Sorry for my delayed return to this. After having posted this, I realized I got this situation a bit wrong. It turns out what I really need is a FileResult that generates a PDF and then returns it to the browser, and for a visual indicator to be shown to the user while the file is generated.

In my opinion, this is an entirely different question. So, I'll post it as a separate question.

¿Fue útil?

Solución

Using an Asynchronous Controller would free up IIS request threads but it won't help you solve your problem. You probably don't need to do much research in this area if you're not already concerned about many long-running user requests coming in at once (enough to starve the IIS request pool).

If you have a traditional Post, Redirect, Get flow, you can easily show a spinner on post which will go away when the browser gets its response and redirects to the new page.

If you are using asynchronous JavaScript, you can show the spinner in your call and hide it in your completion handler.

I would suggest you don't try to start your own background tasks or threads from IIS because it can recycle your app domain at any time. With a user request it will normally drain stop but it won't know about any additional background tasks you start. You can register your object with IIS so it will first notify you before it intends to tear your app domain down but you can only hold off for up to 30 seconds before IIS will continue anyway. Since your work typically takes longer than this to complete, I'm suggesting this more complicated route is probably not going to work well for you and will make your code a bit more complex.

It really comes down to your requirements. Is it alright to make the user wait for the work to complete before they can continue using your application or do they need to be able to continue to interact while waiting for the work to complete?

If you can make them wait, go with the Post, Redirect, Get pattern and a modal spinner you pop up on submit.

If the user must be able to keep working, you're going to need a PartialResponse action and AJAX calls with handlers to show and hide a non-modal spinner to indicate work is in progress.

Otros consejos

You could simply have a page, then kick off an AJAX request to your action. When you kick off the request, show a spinner.

You would then hide the spinner when the AJAX request succeeds or errors out.

With this approach, you would not need to have the Action method be asynchronous since you are not tying up the UI.

This is an example of an approach I take with longer running processes to avoid blocking the UI thread and giving visual feedback to the user.

I will demonstrate with a generating report example, this requires .net 4.5.

Create a method around the report generating processing that returns a task.

As an example please see below:

private Task RunReport() 
{
    return Task.Run(() => 
              // The below should contain your generate report code
              Thread.Sleep(5000)
     );
}

Then make your Action return a task and put an await before calling the report.

This will unblock the UI thread allowing users to continue using the site while the report is being generated. The content is what will be returned to page when it has finished processing.

[HttpPost]
public async Task<ActionResult> RequestReportGen()
{
    await RunReport();
    // Add your link to the report in the content below
    return Content("Report generated!";
}

Include the following javascript libraries:

<script src="@Url.Content("~/Scripts/jquery-1.8.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>

Then use an ajax form to post to your method, please note the LoadingElementId (displays when waiting) and the UpdatedTargetId (displays the finished message).

 @using (Ajax.BeginForm("RequestReportGen", "Home", new AjaxOptions { UpdateTargetId = "result", LoadingElementId="loading" }))
 {
        <div id="loading" style="display:none;">Generating Report...</div>
        <div id="result"></div>
        <input type="submit" />
}

Using the above method your user can still use the site as your UI thread is not blocked and messages are relayed back to the user when loading and on completion.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top