How to answer a request but continue processing code in WebApi - c#

I would like to answer a request, but continue processing code.
I tried something like:
[HttpPost]
public async Task<HttpResponseMessage> SendAsync(MyRequest sms)
{
await Task.Run(() => Process(sms)); //need to run in a separate thread
var response = new MyRequest(sms) { Ack = true };
return Request.CreateResponse(HttpStatusCode.Created, response.ToString());
}
private async void Process(MyRequest sms)
{
var validationResult = new MyRequestValidation(_p2pContext, _carrierService).Validate(sms);
if (string.IsNullOrWhiteSpace(validationResult.Errors[0].PropertyName)) // Request not valid
return;
Message msg;
if (validationResult.IsValid)
{
msg = await _messageService.ProcessAsync(sms);
}
else // Create message as finished
{
msg = _messageService.MessageFromMyRequest(sms,
finished: true,
withEventSource: validationResult.Errors[0].CustomState.ToString()
);
}
// Salve in db
_p2pContext.MessageRepository.Create(msg);
_p2pContext.Save();
}

I would like to answer a request, but continue processing code.
Are you sure you want to do this in ASP.NET? This is not a situation that ASP.NET (or any web server) was designed to handle.
The classic (correct) way to do this is to queue the work to a persistent queue and have a separate backend process do the actual processing of that work.
There are all kinds of dangers with doing processing inside of ASP.NET outside of a request context. In general, you can't assume that the work will ever actually be done. If you're OK with that (or just like to live dangerously), then you can use HostingEnvironment.QueueBackgroundWorkItem.
I have a blog post that goes into more detail.

Related

Async/Await Usage in .NET

I've been working on a project and saw the below code. I am new to the async/await world. As far as I know, only a single task is performing in the method then why it is decorated with async/await. What benefits I am getting by using async/await and what is the drawback if I remove async/await i.e make it synchronous I am a little bit confused so any help will be appreciated.
[Route("UpdatePersonalInformation")]
public async Task<DataTransferObject<bool>> UpdatePersonalInformation([FromBody] UserPersonalInformationRequestModel model)
{
DataTransferObject<bool> transfer = new DataTransferObject<bool>();
try
{
model.UserId = UserIdentity;
transfer = await _userService.UpdateUserPersonalInformation(model);
}
catch (Exception ex)
{
transfer.TransactionStatusCode = 500;
transfer.ErrorMessage = ex.Message;
}
return transfer;
}
Service code
public async Task<DataTransferObject<bool>> UpdateUserPersonalInformation(UserPersonalInformationRequestModel model)
{
DataTransferObject<bool> transfer = new DataTransferObject<bool>();
await Task.Run(() =>
{
try
{
var data = _userProfileRepository.FindBy(x => x.AspNetUserId == model.UserId)?.FirstOrDefault();
if (data != null)
{
var userProfile = mapper.Map<UserProfile>(model);
userProfile.UpdatedBy = model.UserId;
userProfile.UpdateOn = DateTime.UtcNow;
userProfile.CreatedBy = data.CreatedBy;
userProfile.CreatedOn = data.CreatedOn;
userProfile.Id = data.Id;
userProfile.TypeId = data.TypeId;
userProfile.AspNetUserId = data.AspNetUserId;
userProfile.ProfileStatus = data.ProfileStatus;
userProfile.MemberSince = DateTime.UtcNow;
if(userProfile.DOB==DateTime.MinValue)
{
userProfile.DOB = null;
}
_userProfileRepository.Update(userProfile);
transfer.Value = true;
}
else
{
transfer.Value = false;
transfer.Message = "Invalid User";
}
}
catch (Exception ex)
{
transfer.ErrorMessage = ex.Message;
}
});
return transfer;
}
What benefits I am getting by using async/await
Normally, on ASP.NET, the benefit of async is that your server is more scalable - i.e., can handle more requests than it otherwise could. The "Synchronous vs. Asynchronous Request Handling" section of this article goes into more detail, but the short explanation is that async/await frees up a thread so that it can handle other requests while the asynchronous work is being done.
However, in this specific case, that's not actually what's going on. Using async/await in ASP.NET is good and proper, but using Task.Run on ASP.NET is not. Because what happens with Task.Run is that another thread is used to run the delegate within UpdateUserPersonalInformation. So this isn't asynchronous; it's just synchronous code running on a background thread. UpdateUserPersonalInformation will take another thread pool thread to run its synchronous repository call and then yield the request thread by using await. So it's just doing a thread switch for no benefit at all.
A proper implementation would make the repository asynchronous first, and then UpdateUserPersonalInformation can be implemented without Task.Run at all:
public async Task<DataTransferObject<bool>> UpdateUserPersonalInformation(UserPersonalInformationRequestModel model)
{
DataTransferObject<bool> transfer = new DataTransferObject<bool>();
try
{
var data = _userProfileRepository.FindBy(x => x.AspNetUserId == model.UserId)?.FirstOrDefault();
if (data != null)
{
...
await _userProfileRepository.UpdateAsync(userProfile);
transfer.Value = true;
}
else
{
transfer.Value = false;
transfer.Message = "Invalid User";
}
}
catch (Exception ex)
{
transfer.ErrorMessage = ex.Message;
}
return transfer;
}
The await keyword only indicates that the execution of the current function is halted until the Task which is being awaited is completed. This means if you remove the async, the method will continue execution and therefore immediately return the transfer object, even if the UpdateUserPersonalInformation Task is not finished.
Take a look at this example:
private void showInfo()
{
Task.Delay(1000);
MessageBox.Show("Info");
}
private async void showInfoAsync()
{
await Task.Delay(1000);
MessageBox.Show("Info");
}
In the first method, the MessageBox is immediately displayed, since the newly created Task (which only waits a specified amount of time) is not awaited. However, the second method specifies the await keyword, therefore the MessageBox is displayed only after the Task is finished (in the example, after 1000ms elapsed).
But, in both cases the delay Task is ran asynchronously in the background, so the main thread (for example the UI) will not freeze.
The usage of async-await mechanism mainly used
when you have some long calculation process which takes some time and you want it to be on the background
in UI when you don't want to make the main thread stuck which will be reflected on UI performance.
you can read more here:
https://learn.microsoft.com/en-us/dotnet/csharp/async
Time Outs
The main usages of async and await operates preventing TimeOuts by waiting for long operations to complete. However, there is another less known, but very powerful one.
If you don't await long operation, you will get a result back, such as a null, even though the actual request as not completed yet.
Cancellation Tokens
Async requests have a default parameter you can add:
public async Task<DataTransferObject<bool>> UpdatePersonalInformation(
[FromBody] UserPersonalInformationRequestModel model,
CancellationToken cancellationToken){..}
A CancellationToken allows the request to stop when the user changes pages or interrupts the connection. A good example of this is a user has a search box, and every time a letter is typed you filter and search results from your API. Now imagine the user types a very long string with say 15 characters. That means that 15 requests are sent and 15 requests need to be completed. Even if the front end is not awaiting the first 14 results, the API is still doing all the 15 requests.
A cancellation token simply tells the API to drop the unused threads.
I would like to chime in on this because most answers although good, do not point to a definite time when to use and when not.
From my experience, if you are developing anything with a front-end, add async/await to your methods when expecting output from other threads to be input to your UI. This is the best strategy for handling multithread output and Microsoft should be commended to come out with this when they did. Without async/await you would have to add more code to handle thread output to UI (e.g Event, Event Handler, Delegate, Event Subscription, Marshaller).
Don't need it anywhere else except if using strategically for slow peripherals.

Acknowledge the web api call before processing in Web Api C#

I have a main web app which sends the alert to second application and the second application here needs to do more work, meanwhile main application will be waiting for the acknowledgment, hence once the second app receives the request, it should acknowledge by sending a flag and then continue its work.
In order to achieve that i am creating a new method and processing the data there and i am calling that new method using new thread and immediately returning the acknowledgement to main app.
Below is my code, am i following the right approach or is there a better way of doing it ?
public bool Post([FromBody] string content)
{
if (!string.IsNullOrEmpty(content))
{
Thread thread = new Thread(() => AnotherMethod(content));
thread.Start();
}
return true;
}
You could try using async / await
public async Task<bool> Post([FromBody] string content)
{
if (!string.IsNullOrEmpty(content))
{
await AnotherMethod(content);
}
return true;
}
then your AnotherMethod must be async too

How to cancel .Net Core Web API request using Angular?

I have the following two applications
Angular 6/7 App
.Net Core Web API
I am making GET request to API using Angular's HttpClient as shown below
this.subscription = this.httpClient.get('api/Controller/LongRunningProcess')
.subscribe((response) =>
{
// Handling response
});
API controller's LongRunningProcess method has the following code
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
try
{
// Dummy long operation
await Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
// Option 1 (Not working)
if (cancellationToken.IsCancellationRequested)
break;
// Option 2 (Not working)
cancellationToken.ThrowIfCancellationRequested();
Thread.Sleep(6000);
}
}, cancellationToken);
}
catch (OperationCanceledException e)
{
Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
}
return Ok();
}
Now I want to cancel this long-running process so I am unsubscribing from client side as shown below
// On cancel button's click
this.subscription.unsubscribe();
Above code will cancel the request and I can see it is canceled in the Network tab of the browser as shown below
But it is not going to make IsCancellationRequested to true in the method LongRunningProcess of the API, so the operation will keep going.
[Note]: Both Option 1 and Option 2 in API method are not working even if I make a call using postman.
Question: Is there any way to cancel that LongRunningProcess method's operation?
When angular cancel request, you can get cancellation token from http context
CancellationToken cancellationToken = HttpContext.RequestAborted;
if (cancellationToken.IsCancellationRequested)
{
// The client has aborted the request
}
You dont need break in this case only use like this
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested();
// Dummy long operation
await Task.Factory.StartNew(() => Thread.Sleep(60000));
}
return Ok();
}
You can read it more here
This is because your dummy long operation does not monitor the canncellationToken. I'm not sure it is actually your intention to start 10 one-minute tasks all in parallel without any delay, which is what your code does.
In order to have a dummy long operation, the code would be like
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
// Dummy long operation
await Task.Run(() =>
{
for (var i = 0; i < 60; i++)
{
if (cancel.IsCancellationRequested)
break;
Task.Delay(1000).Wait();
}
});
return Ok();
}
Task.Run is just equivalent to Task.Factory.StartNew, by the way.
However, if you just need a dummy long-run operation in your web API, then you can also simply use Task.Delay, which supports cancellation token. Task.Delay throws an exception when the request is canceled, so add exception handling code when you need to do something after request cancellation.
[HttpGet]
[Route("LongRunningProcess")]
public async Task<IActionResult> LongRunningProcess(CancellationToken cancellationToken)
{
// Dummy long operation
await Task.Delay(60000, cancel);
return Ok();
}
Any http observables still running at the time will complete and run their logic unless you unsubscribe in onDestroy(). Whether the consequences are trivial or not will depend upon what you do in the subscribe handler. If you try to update something that doesn't exist anymore you may get an error.
Tip: The Subscription contains a closed boolean property that may be useful in advanced cases. For HTTP this will be set when it completes. In Angular it might be useful in some situations to set a _isDestroyed property in ngDestroy which can be checked by your subscribe handler.
Tip 2: If handling multiple subscriptions you can create an ad-hoc new Subscription() object and add(...) any other subscriptions to it - so when you unsubscribe from the main one it will unsubscribe all the added subscriptions too.
So, best practice is to use takeUntil() and unsubscribe from http calls when the component is destroyed.
import { takeUntil } from 'rxjs/operators';
.....
ngOnDestroy(): void {
this.destroy$.next(); // trigger the unsubscribe
this.destroy$.complete(); // finalize & clean up the subject stream
}
var cancellationToken = new CanellationToken();
cancellationToken.CancelAfter(2000);
using (var response = await _httpClient.GetAsync("emp",
HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token))
{
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();
var emp = await JsonSerializer.DeserializeAsync<List<empDto>>(stream, _options);
}
Further we can also have this "CancellationToken" class, which is nothing much Http client method which terminates the request after certain time-interval.
In angular subscription.unsubscribe(); closes the channel and causes CORE to cancel the API caller's thread, that's good.
Don't use await Task.Run(()... This creates a result/task that should be disposed, if not, the task keeps going, your pattern doesn't permit this - that's why it continues to run.
Simply - 'await this.YourLongRunningFunction()', I'm pretty sure that when the owning thread throws the OperationCancelled exception your task will end.
If "3" doesn't work, then pass a cancellation token to your long running task and set that when you catch your OperationCancelled exception.

Relating Task exception to a Task<T> response

I have just started working with tasks. We have a system setup that uses requests/responses. The service running the tasks accepts a master request that has a list of request objects and returns a master response that has a list of response objects. So it looks something like this
var MasterRequest = new MasterRequest;
MasterRequest.Requests.Add(new BlueRequest);
MasterRequest.Requests.Add(new RedRequest);
MasterRequest.Requests.Add(new YellowRequest);
The request implements a simple IRequest interface and each color is a concrete class. The service has concrete classes (request processors) set up to be able to process each request separately and simultaneously according to a concrete request object. Each concrete class on the service has a GetTask method with a signature like this:
Task<IResponse> GetTask(IRequest);
{
// some setup stuff
return Task.Factory.StartNew<IResponse>(() =>
{
// do task stuff
return response; // implements IResponse
});
}
My service takes the passed in MasterRequest and builds a list of tasks by calling the GetTask call listed above on the concrete request processors. I then use a Parallel.ForEach on the list to process the tasks.
// this is what is returned from the service.
// it has a List<IResponse> on it to hold the resposnes
MasterResposne resposne = new MasterResponse();
List<Task<IResponse>> tasks = new List<Task<IResponse>>();
foreach(IRequest req in MasterRequest.Requests)
{
// factory to get the proper request processor
RequestProcessor p = rp.GetProcessor(req);
tasks.add(p.GetTask(req));
}
Parallel.ForEach(tasks, t =>
{
t.Wait();
// check for faulted and cancelled
// this is where I need help
response.Responses.Add(t.Result);
}
This all works great. But if the task throws an exception I don't know how to tie it back to the specific concrete request that triggered it. I need to know so I can pass back a properly built response to the caller.
My first thought was to subclass Task but that brings up it's own set of issues that I don't want to deal with.
I read this SO article and it seems like I want to do something like this
Is this ok to derive from TPL Task to return more details from method?
I think Reed's second example is my solution but I still cannot see how to run the tasks simultaneously and be able to tie exceptions to the request so I can return a properly built list of responses.
Thanks in advance.
So I was able to use Reed's solution from the link I supplied. My service code to process the requests turned into this
// this is what is returned from the service.
// it has a List<IResponse> on it to hold the resposnes
MasterResposne resposne = new MasterResponse();
List<ExecutionResult> tasks = new List<ExecutionResult>();
foreach(IRequest req in MasterRequest.Requests)
{
// factory to get the proper request processor
RequestProcessor p = rp.GetProcessor(req);
tasks.add(p.GetResult(req));
}
Parallel.ForEach(tasks, t =>
{
t.task.Wait();
response.Responses.Add(t.Result);
}
Where ExecutionResult is defined like so
class ExecutionResult
{
public IResult Result;
public Task<IResponse> task;
}
That gives me access to a pre-built response object so I can pass it back to the caller.
EDIT:
So I reviewed my Parallel.ForEach and was able to redo my code and use await Task.WhenAll as suggested. New code looks more like this:
// this is what is returned from the service.
// it has a List<IResponse> on it to hold the resposnes
MasterResposne resposne = new MasterResponse();
List<ExecutionResult> tasks = new List<ExecutionResult>();
List<ExecutionResult> executionResults = new List<ExecutionResult>();
foreach(IRequest req in MasterRequest.Requests)
{
// factory to get the proper request processor
RequestProcessor p = rp.GetProcessor(req);
ExecutionResult er = engine.GetResult(req);
executionResults.Add(er);
tasks.Add(er.Task);
}
await Task.WhenAll<IResponse>(tasks);
foreach (ExecutionResult r in executionResults)
{
if (r.Task.IsCompleted)
{
response.AddResponse(r.Task.Result);
}
else
{
r.Response.Status = false;
AggregateException flat = r.Task.Exception.Flatten();
foreach (Exception e in flat.InnerExceptions)
{
Log.ErrorFormat("Reqest [{0}] threw [{1}]", r.Response.RequestId, e);
r.Response.StatusReason.AppendLine(e.Message);
}
}
}
This allows me to tie my request information to my task and get the response back that I need to return to my caller.
Thanks for the guidance.
I then use a Parallel.ForEach on the list to process the tasks.
This is actually pretty bad. It's throwing a ton of threads into the mix just to block on the tasks completing.
But if the task throws an exception I don't know how to tie it back to the specific concrete request that triggered it. I need to know so I can pass back a properly built response to the caller.
Whenever you have a "process tasks after they complete" kind of problem, usually the best solution is a higher-level asynchronous operation:
private async Task<IResponse> ProcessAsync(IRequest request)
{
try
{
return await engine.GetResult(request);
}
catch (Exception ex)
{
IResponse result = /* create error response */;
return result;
}
}
This allows a much simpler main function:
MasterResposne resposne = new MasterResponse();
var tasks = MasterRequest.Requests.Select(req => ProcessAsync(req));
response.AddRange(await Task.WhenAll(tasks));

How do I spawn a task that will run to completion and immediately return to the client?

So I'm trying to create a proof of concept for server-side asynch using HTTP 202 codes (where the server accepts the task, immediately returns an endpoint to poll and then creates/updates the resource)
Rick Strahl has a description of how to do this in ordinary ASP.NET. That technique depends on being able to Response.End and then continue to execute code. The Response object doesn't even seem to be available in a Web API controller's context.
If the following worked as planned, it would not block returning the http 202 and still guarantee that the database task will run to completion.
//Insert or Update Asych.
public Task<HttpResponseMessage> Post(bool asynch, [FromBody]DatedValue value) //Insert Value
{
Guid key = Guid.NewGuid();
//Want this to run to completion, even if response associated with parent thread is done.
Task toWait = Task.Factory.StartNew(() =>
{
queue.Add(key, 0);
DatedValue justCreated = Insert(value);
queue[key] = justCreated.Id;
});
//Return address to resource just created.
Task<HttpResponseMessage> sender = Task.Factory.StartNew(() =>
{
HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
message.Headers.Location = new Uri("/ValueQueue/" + key);
return message;
});
Task.WaitAll((new[] { toWait, sender }));
return sender;
}
Task.WaitAll blocks execution and response is not returned until both the tasks are completed. If you change your code something like below, you should be able to return the response while the task is being run.
public HttpResponseMessage Post(bool asynch, [FromBody]DatedValue value)
{
Guid key = Guid.NewGuid();
Task.Factory.StartNew(() =>
{
queue.Add(key, 0);
DatedValue justCreated = Insert(value);
queue[key] = justCreated.Id;
});
HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
message.Headers.Location = new Uri("/ValueQueue/" + key);
return message;
}
However, you should be aware of a problem with an approach like this. If you are hosting Web API on IIS, your worker process can get recycled while your task is running. Since you have already returned the response, as far as ASP.NET is concerned, it has completed its work. So, if for some reason IIS decides to recycle the worker process, it will go ahead regardless of where your task is in terms of execution and hence you can end up with corrupt data.
With .NET 4.5.2, you can use QueueBackgroundWorkItem. Read this - http://blogs.msdn.com/b/webdev/archive/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-long-background-process-in-asp-net.aspx.

Categories

Resources