C# Calling an action asynchronously - c#

I have a form post which should call an action asynchronously.
public async Task<ActionResult> SaveW2Document(Models.DocumentData documentData)
{
some code here
var ID = await Task.Run<int>(() => somObject.SaveDocument(uploadDocument));
//Code that uses ID
if(ID == something){
//Do something
}
}
However it gets called synchronously and my other actions do not execute until this action gets completed.Is it something else that I need to do?

I have a form post which should call an action asynchronously.
I think you're misunderstanding what "asynchronous" means in this context. Using async controller actions (or HTTP request handlers in general) means that the web server can invoke the operations asynchronously and not hold a thread in a wait state while the operation completes. This allows the web server to handle more concurrent requests more effectively.
The browser, however, is waiting for the response. The asynchronous nature of any given operation generally isn't defined by the operation itself, but by whatever invokes it. (For example, note how within this operation there is another asynchronous operation being awaited. If this code didn't await it, the behavior would be very different. But the method being invoked wouldn't change between those two scenarios.)
This makes sense, though. After all, what is the browser going to do if it doesn't wait for the response from the server? Display a blank page?
You can make asynchronous calls to a web server from client-side code by way of using AJAX. That would allow the page to invoke the server-side operation and listen for the response without blocking or changing the context of the page itself. But if the whole page context is posting to the server and waiting to load a new page, it needs to receive that new page before it can display it.

Well I might not have been able to ask the question correctly.But I found the solution to the problem that was occuring. MVC has a behaviour that blocks concurrent requests coming from the same session.I will either have to Disable Session or make it readonly for that controller. Refer this link.
So adding [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)] to my controller helped.

Related

Should sync API calls be wrapped into async? [duplicate]

This question already has answers here:
Wraping sync API into Async method
(2 answers)
Closed 1 year ago.
I read some posts and articles on the internet saying that I should not use async until my calls are IO (reading file, sending request, etc.). But if the calling API is actually an IO-related API and does not support async calls, should I wrap them with Task.Run()?
Say, if I have MVC server calling an external server via API to get data, but the API does not have async methods. Is there a benefit wrapping sync calls with async in such case?
As I understand, if not wrapping, then all calls to the API are sync and each call to my MVC app uses a thread from thread pool. In second case Task.Run() will queue my task onto thread pool, but releases main thread. Is released main thread considered to be benefit here, or it is not worth wrapping? Am I correct here at all?
EDIT
Ok, here are some more details as people requested.
I have a third party .NET assembly that I'm forced to use. I can't just call the web-service directly. It looks pretty much like this:
// the API (I not owner of it, can't change it):
public class Service {
/* everything is synchronous like this. but somewhere inside
it still makes HTTP requests to external server, so that's
kinda IO. just the lib is very old and doesn't provide async methods*/
public Data GetData(Paramters parameters);
}
// here comes my code.
// option 1. sync controller
public class MyController {
public ActionResult GetDataById(string id) {
var data = new Service().GetData(new Parameters{Id = id});
// process and return some result ...
}
}
// option 2. async controller
public class MyController {
public async ActionResult GetDataById(string id) {
var result = await Task.Run(() => new Service().GetData(new Parameters{Id = id}));
// process and return some result ...
}
}
So the question is, does it make sense to do option 2?
By wrapping into async, you may get the benefit of making parallel calls to the external server while the actual call would still remain sync , so it would be like multiple threads each waiting on response from external server. I have done this and it could be beneficial when you would like to handle the failures and then the application would not remain stuck in the calling threads.
Let's say if the API itself performs IO related tasks even then having a free main thread would benefit you if the user chooses to perform multiple tasks through the API you would benefit by having the main thread free rather than having it blocked on a single request.

How to covert async call to work as sync call in C#

I'm a newbie in C#, and I'm going to develop a small program using a third party network library to send the requests.
Suppose there have some requests (just simple strings) stored in the queue qTasks, and it will handle those requests one by one with the order as submitted, the queue can be updated during execution, and it should be stopped whenever there has error returned.
I can just use a for loop to call the send request command in the array one by one, but unfortunately the sendrequest command is an async method with callback OnStageChanged, and I need to check the result before sending the next request when the status is "Done".
I'm now using the following method to handle it:
In the main UI Thread,
// Put those request text in a queue names qTasks, then call a goNextTask() to process the request one by one.
// The queue can be updated by the UI thread at anytime, goNextTask will be called periodically to handle those pending request in the queue.
private void goNextTask(bool lastSuccess = true)
{
if (lastSuccess)
{
if (qTasks.Count > 0)
{
// continue to next request
string requestText = qTasks.Dequeue();
SendRequest(requestText, OnStageChangeHandler);
} else {
// Report for all request sent successfully
}
} else {
// stop and show error
}
}
The callback method OnStageChangeHandler will be called by the library whenever the stage changes, and it will have state "Done" when completed.
private void OnStageChangeHandler(object sender, StageChangeEventArgs e)
{
if (e.newState == SessionStates.Done)
{
// check result here
bool success = <...>
// then call the goNextTask in UI thread with the result of current request.
Application.Current.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal,
(Action)(() => goNextTask(success)));
}
}
Although it works fine now, I think it's a little bit stupid as it has a somewhat recursive flow (A -> B -> A -> B ->....).
I learnt that MS has improved the web request handling, so that it can work in sync mode.
I'd like to know if I can have a wrapper to make the above async call work as a sync call, so that it can be done in a simple flow as a loop like that:
while (qTaks.Count > 0)
{
if (!sendAndWaitReturn(qTasks.Dequeue())) {
// Report error and quit
}
}
// all tasks completed
This sendAndWaitReturn method will send the request, then wait for the status "Done", and then return the result.
I found some example that may use a control flag to indicate the status of the current request, and the callback function will update this control flag, while the UI thread will loop on this flag using a while loop:
while (!requestDone);
so that it will not continue to nextRequest until requestDone. But in this case, the UI will be blocked.
Is there any better way to convert the async call to work as a sync call without blocking the UI thread?
The difficulty you're going to run into is you have conflicting desires. On one hand, you want to avoid blocking the UI thread. On the other hand, you don't want to run things asynchronously and so you're going to block the UI thread.
You're going to have to pick one, and there's absolutely no reason to keep on doing things synchronously (especially in light of blocking the UI thread). If it hurts when you do that, don't do that.
You haven't specified, but I'm guessing that you're starting this processing from a button click event. Make the method invoked by that click event async. For example:
private async void StartProcessing_Click(object sender, EventArgs e)
{
await Task.Run(() => StartProcessing());
}
There, you've started processing and the UI thread isn't tied up.
The next thing is that, you're right, having the event behave in that cyclical manner is silly. The event is to notify someone that the state has changed, its goal isn't to manage queue policy. The queue should manage queue policy (or if you'd rather not abstract that out, the method that processes requests).
So how would you do that? Well, you've said that SendRequest hands the session object back to the caller. The caller is presumably the one who is orchestrating queue policy and determining whether or not to call SendRequest again.
Have the caller check the session object for validity and decide whether to keep going based on that.
Additionally, I'm unfamiliar with that particular library, but briefly glancing at the documentation it looks like there's also a SendRequestAndWait() method with the same signature and that sounds like it might better meet your needs.

ASP.NET MVC: Why is the await keyword blocking my method despite having the task completed?

We've got an ASP.NET MVC application that has a form for visiters to subscribe their details for a newsletter.
The method responsible for subscribing users is defined below. We also have a custom web API that we're contacting through this method.
public async Task<string> Subscribe(User user)
{
if (user== null) throw new ArgumentNullException("user");
var request = new RestRequest("subscribe", Method.POST)
{
RequestFormat = DataFormat.Json
};
request.AddBody(user);
// Service Url is defined further up the code.
var client = new RestClient(serviceUrl);
var response = await client.ExecuteTaskAsync<User>(request);
return response.Data.Id;
}
The code itself works as it returns the appropriate ID from the request and I have confirmed this. The issue is that on the website it's still loading as if the submit button from our newsletter is still processing.
I've got a sneaky suspicion that await is still waiting and therefore hasn't completed it's run but it's confusing me how the results have returned in our CMS but the website is still loading.
Does anyone have any idea?
Await doesn't block your method, but the action won't complete until you get the result from the remote server i.e until the task finishes. The point of await is to free CPU threads while waiting on I/O operations.
So, while you're awaiting the thread which handles your request is free to handle other requests. await is a backend optimization, for the UI client nothing changes.
The reason why it seems that my contact form in the UI is stuck is because the method Subscribe is called in a method that doesn't utilize the async/await keywords.
public string Test(User user)
{
if (user == null) throw new ArgumentNullException("user");
Subscribe(user);
// Rest of the method...
}
The await waits for the Subscribe task to finish and continues. When the method continue's it will do so in a context.
Just as described in this post here:
In the first case, this context is a UI context (which applies to any
UI except Console applications). In the second case, this context is
an ASP.NET request context.
One other important point: an ASP.NET request context is not tied to a
specific thread (like the UI context is), but it does only allow one
thread in at a time. This interesting aspect is not officially
documented anywhere AFAIK, but it is mentioned in my MSDN article
about SynchronizationContext.
So the Test method in this instance is using the RequestContext, calls the Subscribe method. The await starts in the UI context and while waiting it continues back to the RequestContext which it can't access because the Test method is currently using it. It ends in a deadlock. That's why the UI is stuck in loading.

Web API async method with AngularJS

I'm new to using AngularJS with MVC 5 and I've been looking at using Web API with AngularJS since it seems like a good solution for loading data into your client side models.
However I've noticed that quite a few guides use async actions that returns Task<Model> and I don't understand what benefit this gives you over using just standard Web API actions (examples: http://monox.mono-software.com/blog/post/Mono/233/Async-upload-using-angular-file-upload-directive-and-net-WebAPI-service/ and http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/build-a-single-page-application-%28spa%29-with-aspnet-web-api-and-angularjs).
Since these calls to Web API are asynchronous anyway I don't know why we need to make these method calls asynchronous. Wouldn't it be better to use just standard Web API calls?
I don't know if stackoverflow is the right place for this but I was hoping for an explanation on why calls are done this way.
The benefit of async-await on the server is scalability and performance. When you replace synchronous (blocking) operations with asynchronous (non-blocking) ones you free up threads that were previously blocked waiting which you can use to handle other requests concurrently.
async-await enables you to do more with less resources.
Let's assume we have this synchronous method:
public void Process()
{
for (int i = 0; i < 1000; i++)
{
Math.Sqrt(i);
}
Thread.Sleep(3000); // sync I/O
}
By making it async:
public async Task ProcessAsync()
{
for (int i = 0; i < 1000; i++)
{
Math.Sqrt(i);
}
await Task.Delay(3000) // async I/O
}
We can use a single thread to process multiple requests of the same method because the thread handling the request is freed up when you await the asynchronous operation.
To give another illustrative example:
Imagine 2 requests, one of which gets caught up in waiting for database for 2 seconds and the other gets caught up in application logic for 2 seconds.
Without async, at 1 second into the requests, 2 threads are in use.
With async (implemented all the way down to the database call), at 1 second into the requests, 1 thread is in use, processing application logic. The other request is in an await state (no thread used).
Both requests will take 2 seconds, but in the async scenario the number of active threads is reduced for some of the request duration
Async await on the server side is not to enable/enhance asynchronous XMLHttpRequests (AJAX calls). It is to enable the await keyword and handling of methods that return Tasks in lower-level framework code and your server-side code, should you choose to implement any. This example comes to mind:
The underlying Web API's controller dispatcher spins up a controller in response to a request and and tells it to go execute an action. It then needs to look at the response of that and take action depending on the result (error, etc.). If it executes that dispatch synchronously, the thread is blocked by the dispatcher waiting for the action to complete, when it doesn't have to be. In an async context, that dispatcher can await the response from the controller, and the thread is freed up for other tasks to run. When the original controller's action is done, any free thread can pick up where the first thread left off and handle the rest of the dispatch.

How can I redirect to another action from my Asynchronous Action in MVC4

I am currently trying to write some code that will accept some FTP details, get a file list, then allow the user to download the files.
This all works fine, except that I have to wait for the files to finished downloading before it will do anything. I am using an Asynchronous controller as I thought this was meant to help with this sort of thing.
Please see a selection of my code below, I have left out all the code thats not relevant:
[HttpPost]
public ActionResult FtpAsync(Downloads model)
{
var ftpAddress = model.Url;
if (!ftpAddress.StartsWith("ftp://"))
ftpAddress = String.Concat("ftp://", ftpAddress);
var serverPath = Server.MapPath(ThumbnailSupport.CreateBaseVirtualPathForClient(StandardFileLinks.DropBoxLocation,
_website.Client));
if (!Directory.Exists(serverPath))
Directory.CreateDirectory(serverPath);
foreach (string file in model.SelectedFiles)
{
var webClient = new WebClient();
AsyncManager.OutstandingOperations.Increment();
AsyncManager.Parameters["FilesToDownload"] = model.SelectedFiles;
webClient.Credentials = new NetworkCredential(model.Username, model.Password);
webClient.DownloadFileAsync(new Uri(ftpAddress + "/" + file), serverPath + "\\" + file);
AsyncManager.OutstandingOperations.Decrement();
}
return RedirectToAction("Transfer");
}
public ActionResult FtpCompleted(Downloads model)
{
return RedirectToAction("Complete");
}
public ActionResult Transfer()
{
return PartialView();
}
It fires the FtpCompleted action perfectly fine, but the problem is this is meant to handle file transfers of potentially GB's of information. I dont want the user to sit watching a spinning disc while they wait for the files to be downloaded. Hence the reason I was trying to redirect them to the Transfer Action, this action just displays a message telling them that the transfer may take a while and they will be notified once its complete. However this action never actually gets called. I step through the code in debug and it calls it, but it never displays the message and it never gets to the browser according to FireBug.
Am I doing something stupid or is it just not possible to do this?
I would be grateful for any assistance people can offer here as I am completely stuck after browsing google and other posts on here. Even my boss who is a much more experienced coder isnt sure how to handle this.
Thanks in advance,
Gaz
As explained in the documentation an asynchronous controller action consists of 2 methods:
A method which returns void and has the Async suffix and the action name prefix
A method which returns ActionResult and has the Completed suffix and the action name prefix
The first method triggers asynchronous operation and returns immediately. The second method is invoked once the entire operation has completed.
So here's are the correct action signatures:
[HttpPost]
public void FtpAsync(Downloads model)
{
...
}
public ActionResult FtpCompleted(Downloads model)
{
return Content("Complete");
}
Now, if you don't want to freeze the browser during the entire operation you will have to invoke the first controller action using AJAX. Asynchronous controllers do not change anything in terms of the HTTP protocol. From the client perspective it is absolutely the same. The only difference with a standard controller action is that you are not jeopardizing a worker thread during the entire operation. You are now relying on the IOCP (IO/Completion Ports) that are an OS artifact used by the WebClient. The idea is that when you start an IO intensive operation, an IOCP port is created and the controller action immediately returns the thread to the ASP.NET thread pool. Then the operation could last for hours, and once it completes the IOCP is signaled, a pool is drawn from the thread pool and the Completed action is invoked on this thread. So in terms of threads it is very effective. But the total time of execution is absolutely the same as if you have used a standard controller action. Many people think that because Asynchronous Controller are called Asynchronous they run asynchronously from the client perspective. That's a false impression. Asynchronous actions don't make your operations miraculously run faster. They are still perfectly synchronous from the client perspective, because that's how the HTTP protocol works.
So you have 2 possibilities:
Invoke the controller action using AJAX to avoid blocking the browser.
Use COMET/PUSH technology in which it is the server that notifies the client. For example in HTML5 there's the WebSockets standard which could be used to achieve that. In the .NET world SignalR is a great framework which encapsulates this PUSH technology.
The second approach is recommended if you want a really effective solution.

Categories

Resources