Confused about running async methods in the background - c#

Say I have the following methods, in my ASP.NET 5 Web API:
//(in a different class)
public static async Task SendStatus(string message)
{
await SendStatusMessage(message); //sends a status message to the client
}
[HttpGet("api/dowork")]
public async Task<IActionResult> DoWork()
{
await SendStatusToClient("starting to do work!");
//when it reaches this point, it should run the code in the background
await DoTheActualWork();
await SendStatusToClient("finished work!");
//I want the control back after it starts running the above code in the background, to send a 200 status code
return Ok();
}
This is how it should run:
It sends a status message, stating that it has started doing the work.
It does the work, and after the work is done, sends a status message. All of this should be done in the background.
Return a 200 (OK) status code to the user who requested the work. The actual work takes a significant amount of time and should still be running in the background.
How would I achieve this?

There are two answers helpful to you.
The first one, how to do it: Webapi2 - Return from controller action after one task completes, but continue with further Async processing
The second one, why not do it: How to answer a request but continue processing code in WebApi
So what to do now. I would suggest the following steps:
Create a queue. Either a database table an azure service bus queue, a rabbitmq queue or even a simple file or just a static memory structure. (Production loads require some persistence though)
Create a long running task to process the queued items. Web api supports background tasks, but you could create a service a job on the cloud or a windows service or any other application or service that works.

Related

Not able to update UI after background task completion in my ASP.NET application

I am running a long running function as background task in my asp.net application. Before the task completes the main thread exits (I want it that way only, since if I use the await keyword and make main thread wait till background task completes, I get proxy error with message
Proxy Error
The proxy server received an invalid response from an upstream server since the background task is too long
But once after the task completion neither I am able to refresh the page by redirecting to the same page or neither I am able to override the UI. Is there any way to update UI after main thread completes execution.
My code goes like this:
protected void btnImport_Click(object sender, EventArgs e)
{
var task = ImportThread();
if (task.IsCompleted)
{
DisplaySuccess("Import success");
}
else
DisplayError("Import failed");
}
private async Task<bool> ImportThread()
{
try
{
var success = await Task<bool>.Run(() => new Manager().Import().ConfigureAwait(false);
if (task.IsCompleted)
{
DisplaySuccess("Import success");
}
else
{
DisplayError("Import failed");
}
return true;
}
The above async task awaits the method below which is present in another class.
public bool Import()
{
// some operations here
return true;
}
After this method completes the control return backs to ImportThread() but the code written there to override UI is not updating UI. I need to update UI with the status of import. And also from ImportThread control is not going back to button click event method too.
Please help me with any way to update UI the status of import.
Note: I tried using Redirect.Response in ImportThread() to refresh the page, but that didn't work
Your problem is that you have to grasp and under stand the web page lifecycle here.
You have this case in which the web page is sitting on the users desktop:
Now say the user clicks a button.
You now have this:
var task = ImportThread();
if (task.IsCompleted)
Ok, so the web page is up on the server. You can put in even async awaits until the cows come home, but you STILL HAVE THIS:
So as long as your code runs, or waits, the web page is STILL STUCK UP on the server side. ONLY until code completes and exits does the page travel down to the client side.
AGAIN:
Your code behind cannot halt, and cannot wait for something to finish, since if it does, then the page REMAINS up on the server until processing finished.
THEN AND ONLY THEN does the web page make the trip back down to the client side. This then occurs;
And then the SERVER SIDE PAGE IS TOSSED OUT of memory, and all class variables are DESTROYED!!! The web server is now waiting for ANY USER to post back a page for processing!!
So, if you need to run some kind of long running process?
You have a few choices:
post the page, code behind runs, code behind starts a NEW thread, web page makes trip back to client side. At that point, you need a timer + some type of web method call (ajax) to poll or ask the server if the long running process is done. And since a ajax call does NOT have use of any web controls on that page, or page class variables (remember, AFTER the web page travels down back to client side, the web page is NOT NOT EXISTING web server side in memory, nor is ANY of the class variables existing). So, again, this quite much means some kind of timer, or as noted, a timer + code to call some ajax method. and that long running process will have to VERY likely use session() since you don't have use of controls, or even ViewState.
And you don't necessary have to use a ajax call. You could use a simple JavaScript client side routine with a timer that say clicks a button every 1 or 2 seconds, the code behind runs, and it would then have to get the status of that long running process (again probably from session), and then update the display. And then you could also include code to stop the timer when the status has changed to "done" or whatever.
So code behind does not and will not "update" the web page multiple times. You have ONE round trip, and the code behind must run fast, must finish running, and can't even use a AWAIT command, since then the page will STLL wait, and STILL be stuck up on the server.
If you want to go beyond the simple timer trick approach - which I often use?
Then you need to adopt and introduce into your web site something designed for this type of case -
Thankfully, there is signalR for this purpose, and that no doubt the best option and approach for you, since it is designed for exactly your question and scenario.
SignalR
https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/introduction-to-signalr#:~:text=What%20is%20SignalR%3F%20ASP.NET%20SignalR%20is%20a%20library,process%20of%20adding%20real-time%20web%20functionality%20to%20applications.
If you want to asynchronously notify a user (of completion or failure of anything such as a task), you can use web push notification (using firebase messaging cloud) or SignalR sockets. When you use a background task you lose the main thread and unfortunately, there is no way to respond to the related user.

WebAPI: How can I push processing off so that the requester isn't hanging around for a response?

I created a WebAPI method that will insert data into a database from the contents posted to the method.
My question is how can I push processing off so that the requester isn't hanging around for a response?
For example:
[HttpPost]
[Route("MyTest/ImportData")]
public HttpResponseMessage AddData([FromBody] XElement xmlBody)
{
try
{
// Verify that I can read the XML..
// Push XML off to another process to insert into database.
UpdateDataBase(xmlBody);
// Notify requester that I accepted the file.
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.InternalServerError);
}
}
What I have works. However it may take up to 4 minutes to insert all of the data. I would like to verify that I can read the xml, push the process off somehow and give the user an OK response.
Delegate the work to some other process.
It could be whatever you want, a service, a command line app, whatever is appropriate.
I would setup a messaging system, have the webapi send all these work actions to a queue and then have a windows service for example process this queue. This will release the pressure on the api and let it handle other requests without waiting for long running tasks to complete. It doesn't have to be anything more complicated than this.
As a suggestion, RabbitMQ is fairly straight forward, but I would suggest you look around and pick whatever you like

System.Net.WebClient - should I use Async

I have a C# web application that makes a web service call, then renders a page for a browser. Following this advice, I chose to use System.Net.WebClient for the request because it had a succint interface and all the control I needed.
WebClient offers me async versions of all the download methods. Should I use them? I don't care if the current user waits. I need the web service result before I can render the page, and I have nothing else to be doing (for her) in the meantime. However I really do care if my server is tied up while one user's web service call completes. If this was javascript, a synchronous web request on the main thread would hold up at least the whole window. Is this the case in asp.net? For reasons outta my control, my web service request is at the bottom of a pile of 15 method calls. Dot I have to convert them all to async to see any advantage?
Generally speaking, async IO won't yield faster per-request response, but in theory it can increase throughput.
public async Task<IActionResult> YourWebApiMethod() {
// at this point thread serving this request is returned back to threadpool and
// awailable to serve other requests
var result = await Call3ptyService();
// once IO is finished we grab new thread from thread pool to finish the job
return result;
}
// thread serving this request is allocated for duration of whole operation
public IActionResult YourWebApiMethod() {
return Call3ptyService().Result;
}
You have only so-many threads in thread pool, if every single one is busy waiting for external service; your web server will stop serving requests. As for your particular problem - try it and you'll find out.

ASP.NET Web Api, Database connection in Threads

I have an issue with using a Database in a thread in my asp.net Application.
When I want to start my application I want to start a thread called "BackgroundWorker" with it, which runs in the background till the whole application is stopped.
The problem is that I have massive problems with the dbContext in the thread.
I I try to start the walker in my Startup.cs in the methods "ConfigureServices" or "Configure" and then initialize the dbContext in the constructor in the Walker like this "dbContext = new ApplicationContext()" it tells me that the connection is not configured, when I try to operate in the while(true) queue on the database.
If I write an own Controller for the Walker which receives a ApplicationContext in his constructor and then starts a Thread like this, if i call this controller once with a GET Request:
public BackgroundWorker(ChronicusContext dbContext)
{
_dbContext = dbContext;
_messageService = new MailMessageService();
}
// GET: api/backgroundworker
[HttpGet]
[Route("start")]
public void StartWorker()
{
//Thread thread = new Thread(this.DoBackGroundWork);
Thread thread = new Thread(() => DoBackGroundWork(this._dbContext));
thread.Start();
}
public void DoBackGroundWork(ChronicusContext _dbContext)
{
while (true)
{
if (_dbContext.PollModels.Any()) //Here is the exception
{
...
}
}
}
Then I receive an System.ObjectDisposedException that the object is already disposed inside the while (true) queue.
I tried those and similar things in many different ways but allways receive exceptions like these two or that the database connection is closed.
Can somebody help me and tell me, how this works?
Thank you!
Generally, server side multithreading for Web Applications does not happen often and is, most times, a huge no no.
Conceptually, your server is "multithreaded", it handles many HTTP requests from clients/users/other servers. For mobile and web architecture/design, your server(s) process multiple requests and your clients are handling asynchronous calls and dealing with waiting for responses from long running calls like your API method StartWorker.
Think of this scenario, you make a request to your WebAPI method StartWorker, the client, making the request is waiting for a response, putting the work on another thread does nothing as the client is still waiting for a response.
For example, let's consider your client an HTML web page with an Ajax call. You call StartWorker via Ajax, you will be loading data into a HTML table. You will desire, from a UX perspective, to put up a progress spinner while that long running StartWorker responds to your HTML Page Ajax call request. When StartWorker responds, the Ajax call loads the HTML table with the StartWorker response. StartWorker has to respond with the data. If StartWorker responds beforehand than you will have to send a push notification, via SignalR, for example, when the other thread completes and has the data you need for the HTML table.
Hopefully, you see, the call to the WebAPI method, takes the same amount of time from a Ajax request/response perspective, so multithreading becomes pointless in this scenario, a most common web application scenario.
You can have your client UI load other UI elements, showing a progress spinner in HTML table UI area, until your database call is complete and responds with the data to your Ajax call. This way your users know things are happening and something is still loading.
If you still need your additional thread in your API for your project needs, I believe you have to be using Entity Framework 6 or greater to support asynchronous queries, see this tutorial:
http://www.codeproject.com/Tips/805923/Asynchronous-programming-in-Web-API-ASP-NET-MVC
UPDATE
Now that I know you need to run a SQL query on a repeating frequency of time, and you have an Azure Web App, what you want to use is Azure Automation if you are using Sql Azure or create a Sql Server Job if you are using a Sql Server instance as your backend
DbContext is not thread safe. You need to create a new context from inside your thread.
public void DoBackGroundWork()
{
ChronicusContext anotherContext= new ChronicusContext();
while (true)
{
if (anotherContext.PollModels.Any())
{
...
}
}
}

How to create Async ActionResult to create async-await pattern for following polling logic?

I have a loop that actually waits for some process for completion of a Job and returns result.
I have MyRestClient.FetchResult(id) and MyRestClient.FetchResultAsync(id) both available to me, which fetches result from some remote service and returns boolean value if it is complete.
public class StatusController: ActionController {
public ActionResult Poll(long id){
return new PollingResult(()=>{
return MyRestClient.FetchResult(id) == SomethingSuccessful;
});
}
}
public class PollingResult : ActionResult{
private Func<bool> PollResult;
public PollingResult(Func<bool> pollResult){
this.PollResult = pollResult;
}
public override void ExecuteResult(ControllerContext context)
{
Response = context.HttpContext.Response;
Request = context.HttpContext.Request;
// poll every 5 Seconds, for 5 minutes
for(int i=0;i<60;i++){
if(!Request.IsClientConnected){
return;
}
Thread.Sleep(5000);
if(PollResult()){
Response.WriteLine("Success");
return;
}
// This is a comet, so we need to
// send a response, so that browser does not disconnect
Response.WriteLine("Waiting");
Response.Flush();
}
Response.WriteLine("Timeout");
}
}
Now I am just wondering if there is anyway to use Async Await to improve this logic because this thread is just waiting for every 5 seconds for 5 minutes.
Update
Async Task pattern usually finishes all work before sending result back to client, please note, if I do not send intermediate responses back to client in 5 seconds, client will disconnect.
Reason for Client Side Long Poll
Our web server is on high speed internet, where else clients are on low end connection, making multiple connections from client to our server and then relaying further to third party api is little extra overhead on client end.
This is called Comet technology, instead of making multiple calls in duration of 5 seconds, keeping a connection open for little longer is less resource consuming.
And of course, if client is disconnected, client will reconnect and once again wait. Multiple HTTP connections every 5 seconds drains battery life quicker compared to single polling request
First, I should point out that SignalR was designed to replace manual long-polling. I recommend that you use it first, if possible. It will upgrade to WebSockets if both sides support it, which is more efficient than long polling.
There is no "async ActionResult" supported in MVC, but you can do something similar via a trick:
public async Task<ActionResult> Poll()
{
while (!IsCompleted)
{
await Task.Delay(TimeSpan.FromSeconds(5));
PartialView("PleaseWait").ExecuteResult(ControllerContext);
Response.Flush();
}
return PartialView("Done");
}
However, flushing partial results goes completely against the spirit and design of MVC. MVC = Model, View, Controller, you know. Where the Controller constructs the Model and passes it to the View. In this case you have the Controller is directly flushing parts of the View.
WebAPI has a more natural and less hackish solution: a PushStreamContent type, with an example.
MVC was definitely not designed for this. WebAPI supports it but not as a mainstream option. SignalR is the appropriate technology to use, if your clients can use it.
Use Task.Delay instead of Thread.Sleep
await Task.Delay(5000);
Sleep tells the operating system to put your thread to sleep, and remove it from scheduling for at least 5 seconds. As follows, the thread will do nothing for 5 secs - that's one less thread you can use to process incoming requests.
await Task.Delay creates a timer, which will tick after 5 seconds. The thing is, this timer doesn't use a thread itself - it simply tells the operating system to signal a ThreadPool thread when 5 seconds have passed.
Meanwhile, your thread will be free to answer other requests.
update
For your specific scenario, it seems there's a gotcha.
Normally, you'd change the surrounding method's signature to return a Task/Task<T> instead of void. But ASP.NET MVC doesn't support an asynchronous ActionResult (see here).
It seems your options are to either:
move the async code to the controller (or to another class with an async-compatible interface)
Use a WebAPI controller, which seems to be a good fit for your scenario.
I have a video encoding in process with third party cloud api, however
my web client (chrome/ie/ff) need to poll result of encoding. If I
simply pass on result for every 5 seconds, web client will need to
make multiple HTTP calls one after another
I think the approach when you're trying to poll the result of the video encoding operation within the boundaries of a single HTTP request (i.e., within your ASP.NET MVC controller method) is wrong.
While you're doing the polling, the client browser is still waiting for your HTTP response. This way, the client-side HTTP request may simple get timed out. It is also a not-so-user-friendly behavior, the user is not getting any progress notifications, and cannot request the cancellation.
I've recently answer a related question about long-running server side operation. IMO, the best way of dealing with it is to outsource it to a WCF service and use AJAX polling. I also answered another related question on how to do the asynchronous long-polling in a WCF service.

Categories