C# don't wait task to finish, but result needed - c#

Simply I want to make an external call to external api which has SenMessageAsync(string message) method and i need to return my client Ok() without waiting it to finish. But on the backend side I need to response of SendMessageAsync() method for continue some process. I mean somehow I need to await it.
Example
try
{
//...
var response = await SendMessageAsync("test"); //dont wait the response, return Ok()
//do something with response
}
catch(MyException ex)
{
//Log
}
catch(Exception ex)
{
//Log
}
Update:
I am updating with one solution I found. Task.Run() (which called fire and forget). Maybe it helps someone. Thanks for answers.
Solution 1:
_ = Task.Run(() => SendMessageAsync(), task =>
{
//We can have exception here if SendMessageAsync() fails
var exception = task.InnerException.Exception;
//Log the exception
}, TaskContinuationOptions.OnlyOnFaulted)

You need a basic distributed architecture, as described on my blog. In summary:
A durable queue. Serialize the work to be done into this queue (e.g., { "operation" = "SendMessage", "data" = "test" }).
A separate backend processor that reads from that queue and does the actual work (e.g., SendMessageAsync(message.data)).
Your backend processor can await the call to SendMessageAsync and then do further processing.

You can use the Task.Run method to run the SendMessageAsync method asynchronously on a separate thread. You can then return Ok() to the client immediately, while the SendMessageAsync method continues to run in the background. To wait for the result of the SendMessageAsync method, you can use the await keyword within the Task.Run method.
Here's an example:
public async Task<IActionResult> MyAction()
{
// Return Ok() immediately to the client
_ = Task.Run(async () =>
{
// Run SendMessageAsync asynchronously on a separate thread
var result = await SendMessageAsync("My message");
// Continue processing using the result of SendMessageAsync
// ...
});
return Ok();
}

Related

Task returning before its completion

I have desktop application in which I am doing some SQL Server queries asynchronously using ADO.NET. There is one case in which I am creating an object and its taking around 5-6 seconds for completion. Now the problem is occurring when I am calling a ADO.NET asynchronous method to fetch data from database. Once it hit await then call is return to main thread without completing other tasks and after some it returning back await but the call is already returned to main thread, due to this I am not getting complete data.
Here is a main caller:
Task.Run<Product>(() => product.GetProduct(item))
.ContinueWith(task =>
{
if (task.IsFaulted)
{
throw;
}
else if (task.Result != null)
{
// here it coming without completing a complete Task
}
})
.ConfigureAwait(false);
GetProduct methods do some more similar Task
public async Task<Product> GetProduct(ProductVariant item)
{
Product product = new Product();
product.Quantity = await GetQuantity(item.Id);
// some other properties
return product
}
The code when it hit it returned to the above task:
public async Task<List<Item>> Test()
{
// preparing query code
sqlDataReader = await sqlCommand.ExecuteReaderAsync();
// code logic after result returned
}
How can I make it so that returned a complete Task?
Based on the code and the comments you should await product.GetProduct(item) call plus I don't know the need of creating a Task (Task.Run and await it) just for it when you probably need the returned Product synchronously.
Your best bet is:
Product retProduct = await product.GetProduct(item);
if (retProduct != null)
{
// Do your logic
}
else
{
// Handle it
}

C# best practice to run an independent tasks in conjunction with awaitable task

Application : Asp.Net Core Web API
I have requirement, On receiving the notification I should do 2 independent tasks. notification gets triggered as apart of web hook.
call another service and then saves the response in log.
Since this is independent task I do not want to wait the caller until we receive response. So I thought to wrap this function in Task.Run(), so that it will be run on another thread available on thread pool.
I donot care wheather it fails or success, I just need to log the response.
Task.Run(() => ProcessThroughRestClient(this.httpContext.Request, this.cspgConfiguration.Value.ApacNotificationService, this.restClient, this.logger));
Saves the request object in DB for tracing.
Since I must save the notification, I made it as awaitable task.
await this.invoiceDbInstance.UpdateItemAsync(payment);
Below is the full code.
Main Method
public async Task<IActionResult> NotifyAsync()
{
this.logger.LogTrace("{CorrelationId} - notify async method has been called.", this.CorrelationId);
Task.Run(() => ProcessThroughRestClient(this.httpContext.Request, this.Configuration.Value.NotificationService, this.restClient, this.logger));
var request = this.GetDataFeedRequest(this.httpContext.Request);
if (request != null)
{
this.logger.LogInformation("{CorrelationId} - data feed invoked with the order reference {OrderReference}", request.OrderReference, this.CorrelationId);
Func<Invoice, bool> condition = x => x.SourceTransactionId == request.OrderReference;
var payment = this.invoiceDbInstance.GetItem(condition);
if (payment != null)
{
payment.TransactionStatus = request.IsSuccessStatusCode() ? TransactionStatus.Success : TransactionStatus.Failure;
payment.TransactionId = request.PaymentReference;
payment.UpdatedAt = DateTime.UtcNow.ToLongDateString();
await this.invoiceDbInstance.UpdateItemAsync(payment);
this.logger.LogInformation("{CorrelationId} - data feed updated order reference {OrderReference} updated in Invoice with {PaymentReference}", request.OrderReference, this.CorrelationId, request.PaymentReference);
}
else
{
this.logger.LogInformation("{CorrelationId} - Payment not found.", this.CorrelationId);
}
}
this.logger.LogTrace("{CorrelationId}- notify async method ended.", this.CorrelationId);
return new OkResult();
}
Http Call function which will be invoked through Task.Run()
private static HttpResponseMessage ProcessThroughRestClient(HttpRequest request, string url, IRestClient client, ILogger<PayDollarNotificationService> log)
{
try
{
log.LogTrace("Paydollar notify async ProcessThroughRestClient method has been called.");
var parameters = request.Form?.ToDictionary(x => x.Key, x => x.Value.ToString());
if (parameters.Any())
{
var response = client.PostAsync(url, RestClientHelper.BuildFormUrlEncodedContent(parameters)).Result;
log.LogInformation("sent request to {url}.", url);
log.LogInformation($"{url} response {response.ReadContent()?.Result}");
return response;
}
else
{
log.LogInformation("No form parameters found.");
return null;
}
}
catch (Exception ex)
{
log.LogError($"An error occured: {ex.Message} {ex}");
}
return null;
}
My question is, Is there any advantage using Task.Run() as above instead awitable task? or is it is going to be blocking thread?

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.

Understanding async/await vs Wait in C# with "ContinueWith" behavior

One method is a standard async method, like this one :
private static async Task AutoRetryHandlerAsync_Worker(Func<Task<bool>> taskToRun,...)
I have tested two implementations, one that use await and the other uses .Wait()
The two implementations are not equal at all because the same tests are failing with the await version but not the Wait() one.
The goal of this method is to "execute a Task returned by the input function, and retry by executing the same function until it works" (with limitations to stop automatically if a certain number of tries is reached).
This works:
private static async Task AutoRetryHandlerAsync_Worker(Func<Task<bool>> taskToRun,...)
{
try {
await taskToRun();
}
catch(Exception)
{
// Execute later, and wait the result to complete
await Task.Delay(currentDelayMs).ContinueWith(t =>
{
// Wait for the recursive call to complete
AutoRetryHandlerAsync_Worker(taskToRun).Wait();
});
// Stop
return;
}
}
And this (with async t => and the usage of await instead of t => and the usage of .Wait() doesn't work at all because the result of the recursive call is not awaited before the final return; is executed :
private static async Task AutoRetryHandlerAsync_Worker(Func<Task<bool>> taskToRun,...)
{
try {
await taskToRun();
}
catch(Exception)
{
// Execute later, and wait the result to complete
await Task.Delay(currentDelayMs).ContinueWith(async t =>
{
// Wait for the recursive call to complete
await AutoRetryHandlerAsync_Worker(taskToRun);
});
// Stop
return;
}
}
I'm trying to understand why this simple change does change everything, when it's supposed to do the exact same thing : waiting the ContinueWith completion.
If I extract the task ran by the ContinueWith method, I do see the state of the ContinueWith function passing to "ranToCompletion" before the return of the inner await completes.
Why? Isn't it supposed to be awaited?
Concrete testable behaviour
public static void Main(string[] args)
{
long o = 0;
Task.Run(async () =>
{
// #1 await
await Task.Delay(1000).ContinueWith(async t =>
{
// #2 await
await Task.Delay(1000).ContinueWith(t2 => {
o = 10;
});
});
var hello = o;
});
Task.Delay(10000).Wait();
}
Why does var hello = o; is reached before o=10?
Isn't the #1 await supposed to hang on before the execution can continue?
The lambda syntax obscures the fact that you ContinueWith(async void ...).
async void methods are not awaited and any errors they throw will go unobserved.
And to your base question, retrying from within a catch is not a recommended practice anyway. Too much going on, catch blocks should be simple. And bluntly retrying for all exception types is also very suspect. You ought to have an idea what errors could warrant a retry, and let the rest pass.
Go for simplicity and readability:
while (count++ < N)
{
try
{
MainAction();
break;
}
catch(MoreSpecificException ex) { /* Log or Ignore */ }
Delay();
}

calling WCF method asynchronously

I'm looking for feedback on the following code. I've read here that I should avoid use of async void.
So as a result I've implemented the following in a method..
foreach (var sample in returns)
{
_logger.Debug("Calling async method");
var resultFromMethodCall = CallMethodAsync(uploadReturn);
_logger.Debug("Continuing....");
}
async Task<Tuple<bool,long>> CallMethodAsync(Sample sampleReturn)
{
try
{
Service1Client client = new Service1Client();
Tuple<bool, long> results = await client.ValidateSampleReturnAsync(sampleReturn);
_logger.Debug("call to Sample Return validator completed for sample: {0}", results.Item2);
return results;
}
catch (Exception ex)
{
_logger.Error(ex, "Error occured while calling WCF service");
return new Tuple<bool, long>(false, sampleReturn.Id);
}
}
When I do nothing with the returned variable resultFromMethodCall, the logging indicates the all is working as I expect. However when I log out items from the variable resultFromMethodCall, it appears that its now running synchronously as it waits for the object to be returned from the call.
Am I missing something obvious here? Or am I completely misunderstanding how this works.
CallMethodAsync is correct.
If you don't await (or Wait) resultFromMethodCall execution will continue while that task is still running. Whether you should allow that depends on what you want to happen.
However when I log out items from the variable resultFromMethodCall it appears that its now running synchronously as it waits for the object to be returned from the call.
If you're using resultFromMethodCall.Result to get the items, then yes, it's blocking and running synchronously.
If you're using await resultFromMethodCall to get the items, then no, it's not running synchronously. However, it is running sequentially - meaning that the method will "pause" at the await and only continue when the resultFromMethodCall task completes.

Categories

Resources