In my program, i'm making a request, and I log the request and the response.
But using the ConfigureAwait(false), may loses the context of my "logger" object, which is logging the request in one file, and the response in another file.
try
{
logger.VerboseRequest(tokenParameters.Endpoint, payloadJson, options);
serializedResponse = await httpHandler.PostAsync<string>
(tokenParameters.Endpoint, payloadJson, options, cts.Token)
.ConfigureAwait(false);
}
catch (TaskCanceledException)
{
throw new TokenTimeoutException();
}
catch (Exception ex)
{
logger.Error(String.Format("Error when posting to Endpoint: {0}",ex.Message));
throw;
}
Any Idea why this is happening? Or what to do to avoid it?
By removing the ConfigureAwait(false) I may have TimeOut problems, so that is not an option.
That's by design behavior, so you must ask yourself: "Am I doing this the right way?"
There are various way's to overcome that problem. If you want a loggable fire-and-forget async call, you can wrap it in a call like this. But beware, the task runs probably on a different context, so you might not have access to typical context-bound variables, like your current HttpContext (and stuff like that).
Task.Run(async () =>
{
try
{
logger.VerboseRequest(tokenParameters.Endpoint, payloadJson, options);
serializedResponse = await httpHandler.PostAsync<string>
(tokenParameters.Endpoint, payloadJson, options, cts.Token);
}
catch (TaskCanceledException)
{
throw new TokenTimeoutException();
}
catch (Exception ex)
{
logger.Error(String.Format("Error when posting to Endpoint: {0}",ex.Message));
throw;
}
}).ConfigureAwait(false);
Or even better: wrap it in a function
async Task DoStuff()
{
try
{
logger.VerboseRequest(tokenParameters.Endpoint, payloadJson, options);
serializedResponse = await httpHandler.PostAsync<string>
(tokenParameters.Endpoint, payloadJson, options, cts.Token);
}
catch (TaskCanceledException)
{
throw new TokenTimeoutException();
}
catch (Exception ex)
{
logger.Error(String.Format("Error when posting to Endpoint: {0}",ex.Message));
throw;
}
}
And where your current code is:
async Task WhereYouAreDoingStuff()
{
DoStuff().ConfigureAwait(false);
}
Related
I am trying to call a async method of ViewModel from another method in View but it is behaving as syncronous.
View Model:
public async Task<bool> GetReadyForUnlockWithQR()
{
try
{
SmilaHash = GetRandomeHashKey();
var data = JsonConvert.SerializeObject(GetSmilaInfo());
var res = await apiClient.PostAsync<String>("readyforunlockwithqr", data);
if (res != null)
{
JObject json = JObject.Parse(res);
if (json["doUnlock"] != null)
{
LoginStatus = json.SelectToken("doUnlock").Value<bool>();
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
CancelPendingRequests();
throw ex;
}
return false;
}
I have my api methods defined in a custome APIClient file. The above request may take a minute to complete. I don't want to stop the UI and perform my further operations in View. Following is my View:
private async void UnlockButton_Click(object sender, RoutedEventArgs e)
{
try
{
await ViewModel.GetReadyForUnlockWithQR();
DisplayQRCode();
}
catch(Exception ex)
{
if (ex is HttpRequestException)
{
Debug.WriteLine("Server not reachable");
MessageBox.Show("Server not reachable");
}
else if (ex is OperationCanceledException)
{
Debug.WriteLine("Timeout exception");
QRCodeImage.Source = null;
QRCodeCanvas.Visibility = Visibility.Hidden;
}
else
{
Debug.WriteLine(ex.Message);
}
}
}
I above code ideally the DisplayQRCode() function should work immediately after await ViewModel.GetReadyForUnlockWithQR(); but it is not happening. The DisplayQRCode() is waiting to receive response from ViewModel.GetReadyForUnlockWithQR() Why is this not behaving as logical asyn code.
The DisplayQRCode() is waiting to receive response from ViewModel.GetReadyForUnlockWithQR() Why is this not behaving as logical asyn code.
The asynchronous method is behaving serially (not "synchronously"), which is exactly what await is supposed to do.
You can think of await as "asynchronous wait": the method is paused and will not continue pass the await until the task completes, but it waits asynchronously, so the thread is freed (the method returns to its caller).
I above code ideally the DisplayQRCode() function should work immediately after await ViewModel.GetReadyForUnlockWithQR(); but it is not happening.
If you want to do that, then you can call GetReadyForUnlockWithQR but don't await the task until after DisplayQRCode completes:
var getReadyTask = ViewModel.GetReadyForUnlockWithQR();
DisplayQRCode();
await getReadyTask;
I'm trying to log a response to a database inside of a delegating handler. Since I want to return the response without waiting for the record to be saved to the database, I want to do that part with an async task.
My problem is, if anything goes wrong within that task, the error cannot be caught and the API crashes with the error
System.Exception: 'Exception of type 'System.Exception' was thrown.'
I've recreated a minimal case below:
public class LogDelegatingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
//send request and get response
var response = await base.SendAsync(request, cancellationToken);
//return response while this runs
_ = Task.Run(() => Log());
//Task.Run(() => Log()); //also crashes
//await Task.Run(() => Log()); //also crashes
return response;
}
catch (Exception e)
{
//this is never reached
}
}
private async void Log()
{
try
{
//this crashes
throw new Exception("test");
}
catch (System.AggregateException e)
{
//this is never reached
}
}
}
After reading SO and finding a thread where the user did care about exceptions thrown, I wanted to ask this where in my case I don't care, but I do want the response to come through first. After reading this
How to safely call an async method in C# without await
I added the _ to dispose of the task and not await it.
That's because you are throwing an Exception but attempt to catch AggregateException.
Try this:
try
{
//this crashes
throw new Exception("test");
}
catch (System.AggregateException e)
{
// handle in case of aggregate exception
}
catch (Exception ex)
{
// in case of other exceptions
}
The catch in SendAsync() is never reached because Log() is async void instead async Task.
You should only ever use async void for fire-and-forget stuff like event. If you want anything back from it, even exceptions, you need to use a Task
Welp, I've this code:
public static async Task TimedSync (CancellationToken ct)
{
try {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
await Task.Run(async () => await UP.Sincronizacao.SyncDB(true));
Xamarin.Forms.Device.StartTimer(TimeSpan.FromMinutes(1), () => {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
Task.Run(async () => await UP.Sincronizacao.SyncDB(false));
return true;
});
} catch (OperationCanceledException) {
await Current.MainPage.DisplayAlert("Got it", "Good", "ok");
} catch (Exception e) {
await Current.MainPage.DisplayAlert("Oops", e.Message, "dismiss");
}
}
The app just crashes at this point, and on debug I find that the exception thrown by ThrowIfCancellationRequested() is unhandled.
Edit:
Ok, something really weird happened, I removed the first if(ct.IsCancellationRequested) ct.ThrowIfCancellationRequested(); and followed Peter's suggestion, the Throw inside the lambda now throws the exception, the try catch block I put on it as well didn't work, but the try catch outside the lambda caught the exception. Here's the code:
public static async Task TimedSync (CancellationToken ct)
{
try {
await Task.Run(async () => await UP.Sincronizacao.SyncDB(true));
Xamarin.Forms.Device.StartTimer(TimeSpan.FromMinutes(1), () => {
try {
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
Task.Run(async () => await UP.Sincronizacao.SyncDB(false));
return true;
} catch (OperationCanceledException) {
return false;
}
});
} catch (OperationCanceledException) {
await Current.MainPage.DisplayAlert("Got it", "Good", "ok");
} catch (Exception e) {
await Current.MainPage.DisplayAlert("Oops", e.Message, "dismiss");
}
}
It kinda works for me :)
But still would like to understand what's going on here
You are passing StartTimer a lambda that will throw a CancellationException when cancellation happens, but this exception doesn't necessarily fire inside StartTimer or the scope of TimedSync.
My guess, because I don't use Xamarin, is that the timer code running your lambda sees the exception on a separate task and promotes that to an application failure.
If you catch CancellationException in the lambda and return false this should have the desired effect of stopping the timer without propagating an exception to the Xamarin timer code.
Note that the direct call to ct.ThrowIfCancellationRequested() will be caught inside TimedSync and hit your catch block.
Does try catch outside of: await Task.Run(() => make sense or just use them only inside of await?
private async void Test()
{
try
{
await Task.Run(() =>
{
try
{
DoingSomething();
}
catch (Exception ex)
{
log.Error(ex.Message);
}
});
}
catch (Exception ex)
{
log.Error(ex.Message);
}
}
If you handle Exception inside the delegate (in your case just for logging purpose), await will not raise an exception in normal circumstances. This should be fine.
private async Task Test()
{
await Task.Run(() =>
{
try
{
DoingSomething();
}
catch (Exception ex)
{
log.Error(ex.Message);
}
});
}
However, since you are awaiting the Task, most probably, there will be some DoSomethingElse in the Test method, which might be affected by the outcome of the Task - in which case it also makes sense to have a try/catch around await.
private async Task Test()
{
try
{
await Task.Run(() =>
{
try
{
DoingSomething();
}
catch (SomeSpecialException spex)
{
// it is OK to have this exception
log.Error(ex.Message);
}
});
DoSomethingElse(); // does not run when unexpected exception occurs.
}
catch (Exception ex)
{
// Here we are also running on captured SynchronizationContext
// So, can update UI to show error ....
}
}
If the delegate you pass to Task.Run raises an exception, then you can catch it outside the Task.Run when you await the returned task.
You shouldn't think of await as though it was a block. There's no such thing as "inside of await". Instead, think of await as an operator that takes a single argument (in this case, the Task returned by Task.Run). Task.Run will catch exceptions from its delegate and place them on the returned Task; await will then propagate that exception.
You can add try catch to outside code too. The compiler will execute catch section when an exception happens during the async call. Here is more details why would you need try catch around await http://msdn.microsoft.com/en-us/library/vstudio/0yd65esw.aspx look Exceptions in Async Methods
This is the behavior in .Net 6:
try{
await Task.Run(()=> & call whatever method );
}
catch { handle the exception } <-- the catch will never get hit. An unhandled exception in the Task.Run line will happen
However, this will work:
try{
await Task.Run(async ()=> & call some method );
}
catch(Exception ex){
handle the exception
}
The async before the ()=> has to be there for .Net 6
I've just confirmed this.
When using tasks for large/long running workloads that I need to be able to cancel I often use a template similar to this for the action the task executes:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
Log.Exception(ex);
throw;
}
}
The OperationCanceledException should not be logged as an error but must not be swallowed if the task is to transition into the cancelled state. Any other exceptions do not need to be dealt with beyond the scope of this method.
This always felt a bit clunky, and visual studio by default will break on the throw for OperationCanceledException (though I have 'break on User-unhandled' turned off now for OperationCanceledException because of my use of this pattern).
UPDATE: It's 2021 and C#9 gives me the syntax I always wanted:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
Log.Exception(ex);
throw;
}
}
Ideally I think I'd like to be able to do something like this:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (Exception ex) exclude (OperationCanceledException)
{
Log.Exception(ex);
throw;
}
}
i.e. have some sort of exclusion list applied to the catch but without language support that is not currently possible (#eric-lippert: c# vNext feature :)).
Another way would be through a continuation:
public void StartWork()
{
Task.Factory.StartNew(() => DoWork(cancellationSource.Token), cancellationSource.Token)
.ContinueWith(t => Log.Exception(t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
}
public void DoWork(CancellationToken cancelToken)
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
but I don't really like that as the exception technically could have more than a single inner exception and you don't have as much context while logging the exception as you would in the first example (if I was doing more than just logging it).
I understand this is a bit of a question of style, but wondering if anyone has any better suggestions?
Do I just have to stick with example 1?
So, what's the problem? Just throw away catch (OperationCanceledException) block, and set proper continuations:
var cts = new CancellationTokenSource();
var task = Task.Factory.StartNew(() =>
{
var i = 0;
try
{
while (true)
{
Thread.Sleep(1000);
cts.Token.ThrowIfCancellationRequested();
i++;
if (i > 5)
throw new InvalidOperationException();
}
}
catch
{
Console.WriteLine("i = {0}", i);
throw;
}
}, cts.Token);
task.ContinueWith(t =>
Console.WriteLine("{0} with {1}: {2}",
t.Status,
t.Exception.InnerExceptions[0].GetType(),
t.Exception.InnerExceptions[0].Message
),
TaskContinuationOptions.OnlyOnFaulted);
task.ContinueWith(t =>
Console.WriteLine(t.Status),
TaskContinuationOptions.OnlyOnCanceled);
Console.ReadLine();
cts.Cancel();
Console.ReadLine();
TPL distinguishes cancellation and fault. Hence, cancellation (i.e. throwing OperationCancelledException within task body) is not a fault.
The main point: do not handle exceptions within task body without re-throwing them.
Here is how you elegantly handle Task cancellation:
Handling "fire-and-forget" Tasks
var cts = new CancellationTokenSource( 5000 ); // auto-cancel in 5 sec.
Task.Run( () => {
cts.Token.ThrowIfCancellationRequested();
// do background work
cts.Token.ThrowIfCancellationRequested();
// more work
}, cts.Token ).ContinueWith( task => {
if ( !task.IsCanceled && task.IsFaulted ) // suppress cancel exception
Logger.Log( task.Exception ); // log others
} );
Handling await Task completion / cancellation
var cts = new CancellationTokenSource( 5000 ); // auto-cancel in 5 sec.
var taskToCancel = Task.Delay( 10000, cts.Token );
// do work
try { await taskToCancel; } // await cancellation
catch ( OperationCanceledException ) {} // suppress cancel exception, re-throw others
You could do something like this:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (OperationCanceledException) when (cancelToken.IsCancellationRequested)
{
throw;
}
catch (Exception ex)
{
Log.Exception(ex);
throw;
}
}
C# 6.0 has a solution for this..Filtering exception
int denom;
try
{
denom = 0;
int x = 5 / denom;
}
// Catch /0 on all days but Saturday
catch (DivideByZeroException xx) when (DateTime.Now.DayOfWeek != DayOfWeek.Saturday)
{
Console.WriteLine(xx);
}
According to this MSDN blog post, you should catch OperationCanceledException, e.g.
async Task UserSubmitClickAsync(CancellationToken cancellationToken)
{
try
{
await SendResultAsync(cancellationToken);
}
catch (OperationCanceledException) // includes TaskCanceledException
{
MessageBox.Show(“Your submission was canceled.”);
}
}
If your cancelable method is in between other cancelable operations, you may need to perform clean up when canceled. When doing so, you can use the above catch block, but be sure to rethrow properly:
async Task SendResultAsync(CancellationToken cancellationToken)
{
try
{
await httpClient.SendAsync(form, cancellationToken);
}
catch (OperationCanceledException)
{
// perform your cleanup
form.Dispose();
// rethrow exception so caller knows you’ve canceled.
// DON’T “throw ex;” because that stomps on
// the Exception.StackTrace property.
throw;
}
}
I am not entirely sure of what you are trying to achieve here but I think the following pattern might help
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (OperationCanceledException) {}
catch (Exception ex)
{
Log.Exception(ex);
}
}
You might have observed that I have removed the throw statement from here. This will not throw the exception but will simply ignore it.
Let me know if you intend to do something else.
There is yet another way which is quite close to what you have exhibited in your code
catch (Exception ex)
{
if (!ex.GetType().Equals(<Type of Exception you don't want to raise>)
{
Log.Exception(ex);
}
}