.NET 6 WEB API Server side timeout?
I don't want client side task cancellation. Along with client, Should need to cancel server call also (Httpclient/DB call).
I will explain the scenario with example,
I have one console application and inside that I am calling one HttpAPI.
Like:
Static Void Main(){
try {
var res = client.GetAsync("url", cancellationToken);
}
catch(OperationCancellation ex){
}
}
After some period of time I need to stop console app and as well as GetAPI call also. Should stop the GetAPI call is my ultimate goal.
With cancellation token I can able to reject the request only from client side.
Please help me how to do it!!!
}
Related
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.
I'm working on a SignalR scenario where the server needs to call clients methods in parallel.
In my tests, the calls to the same client seem sequentials.
I've seen, with the .Net5 was added a new property on the server, MaximumParallelInvocationsPerClient but this property seems to work only on the client to server calls.
There is any option to call client methods in parallel?
Edit:
Testing with net core 2.1 on the c# client I Saw the calls to the client are sended in parallel, for this reason, the problem isn't on the server but it is on the client.
Thanks,
Since I can't find any property to indicate to process requests in parallel, the best option is run the action as a task without await:
con.On("ClientCommandName", () =>
{
Task.Run(() =>
{
//Code of the action....
});
});
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())
{
...
}
}
}
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.
I'm very new to Web API and I have an unusual pattern that I need to implement. In the Post method of my controller, it is to take an object which includes a CallbackURL. It will then immediately return an HTTP response to the caller. Afterwards, it will use a 3rd party, off-site API to perform some work with the object. Once that work is done, the controller is to post the results of that work to the CallbackURL.
However, I do not know how to implement this in Web API. Once I return the HTTP response, the controller's lifecycle is over, correct? If so, how do I perform the work I need to do after I return the response?
If you only need to post results to a url and not to the client that initiated the call, you could possibly do something as easy as this:
public string MyAPIMethod(object input)
{
Task.Factory.StartNew(() =>
{
//call third-party service and post result to callback url here.
});
return "Success!";
}
The api call will return right away, and the Task you created will continue the processing in a different thread.
Creating a task to finish up the request (as suggested by Jason P above) will most likely solve the problem, thread-safety provided. However that approach might hurt the performance of your Web service if calls to the 3rd party API take a significant amount of time to complete and/or you are expecting many concurrent clients. If that was the case, your problem seems to be the perfect candidate for a service pattern called "Request/Acknowledge/Callback" (also "Request/Acknowledge/Relay"). Using that pattern, your Web API method will just store each request (including the callback URL) into a queue/database and return quickly. A separate module (possibly running on more than one machine, depending on the number and complexity of the tasks) will take care of completing the tasks, and subsequently notifying completion through the callback URL (please see http://servicedesignpatterns.com/ClientServiceInteractions/RequestAcknowledge).
This is presuming you want to return the results of your 3rd-party query to the caller.
You're correct, this is outside of what's possible with WebAPI. Once you return the HTTP Response, the client also has no connection to your server.
You should look into Asp.Net SignalR, which allows a persistent connection between the client and server, working in modern browsers, and even back to IE7 (though officially unsupported), as well as supporting non-browser clients.
You can then do a couple of things, all of which require the client to connect to SignalR first.
Option 1: You can call your WebApi controller, which can return, but not before launching a task. This task can query the 3rd party api, then invoke a function on the caller via SignalR with the results that you want to provide.
Option 2: You can call a SignalR Hub action, which can talk back to your client. You can tell your client the immediate response, query the 3rd-party api, then return the results you want to provide.