The method RelatoriosEstaticos.AbrirDataAtual that is within the Task below is returning an exception already handled in the method itself, the problem is that the Task continues execution of the next line var links = ListArquivos.ListaLinksDownlaod(driver); which depends on the method AbrirDataAtual() to be executed, it also throws an exception. I have tried to treat within the method, put the task inside a Try / catch, but nothing works, there is always the exception in the method ListaLinksDownlaod and should not even get there.
How can I stop the execution of the task, such as when we send a CancellationToken, but this time, when an exception occurs.
private async Task<List<IWebElement>> Acessar(IWebDriver driver, string data, CancellationToken ct)
{
return await Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
LoginNgin.Login(config.User, config.Password, driver);
RelatoriosEstaticos.AbrirRelatoriosEstaticos(driver);
RelatoriosEstaticos.AbrirDataAtual(driver, data);
var links = ListArquivos.ListaLinksDownlaod(driver);
MethodInvoker action = delegate { pgbStatus.Maximum = links.Count(); };
pgbStatus.BeginInvoke(action);
return links;
});
}
It's not possible to tell for sure without seeing actual implementation of AbrirDataAtual, but it definitely looks like this method is handling exception that should not be handled there.
Generally, method should handle exception only if it can handle it properly (by properly I mean it can recover application to the state where program can safely continue, inform user about the error, etc.), otherwise it should not handle it at all and let the exception propagate to the caller(s) of the method.
Based on description of your problem, AbrirDataAtual doesn't (and can't) handle the exception properly, so you should not catch the exception there (or if excetion must be caught there, you should re-throw it). All following methods (including ListArquivos.ListaLinksDownlaod) will be skipped up to the point, where exception is handled. Problem solved!
The following example shows how to handle the exception directly in the task (after you remove exception handling in AbrirDataAtual). But it's likely that it's still not the best place for such exception handler, but again, finding such place would require complete source code so take it just as an example to clarify what I'm talking about:
private async Task<List<IWebElement>> Acessar(IWebDriver driver, string data, CancellationToken ct)
{
return await Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
LoginNgin.Login(config.User, config.Password, driver);
RelatoriosEstaticos.AbrirRelatoriosEstaticos(driver);
try
{
RelatoriosEstaticos.AbrirDataAtual(driver, data);
var links = ListArquivos.ListaLinksDownlaod(driver);
MethodInvoker action = delegate { pgbStatus.Maximum = links.Count(); };
pgbStatus.BeginInvoke(action);
return links;
}
catch (Exception)//Use more specific exception type if possible
{
//Do all neccesary to properly handle the exception
}
});
}
If you still believe that AbrirDataAtual method is the right place to handle the exception, an alternative approach is to modify AbrirDataAtual to return boolean flag indicating success/failure of it's operation, e.g.:
bool AbrirDataAtual(IWebDriver driver, string data)
{
try
{
//Do all the neccessary stuff
...
//Indicate that AbrirDataAtual succeeded
return true;
}
catch(Exception)
{
//Handle exception properly
...
//Indicate that AbrirDataAtual failed
return false;
}
}
private async Task<List<IWebElement>> Acessar(IWebDriver driver, string data, CancellationToken ct)
{
return await Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
LoginNgin.Login(config.User, config.Password, driver);
RelatoriosEstaticos.AbrirRelatoriosEstaticos(driver);
if (RelatoriosEstaticos.AbrirDataAtual(driver, data))
{
//Continue execution
var links = ListArquivos.ListaLinksDownlaod(driver);
MethodInvoker action = delegate { pgbStatus.Maximum = links.Count(); };
pgbStatus.BeginInvoke(action);
return links;
}
else
{
//AbrirDataAtual failed
return null;
//or throw exception if appropriate
throw new Exception();
}
});
}
Related
In my client application that uses webApi I have a plethora of methods that make calls to webApi asynchronously like this:
var task = Task.Run(async () => await this.SaveObject(User));
return task.Result.Content;
The async function can throw an exception if something is wrong with object being saved. In this case my exception will be handled on the client based on it's type. Problem is when async task throws an exception, task.Result.Content buries in within System.AggregateException.
Right now I handle it like this:
try
{
var task = Task.Run(async () => await this.saveObject(User)); return task.Result.Content;
}
catch(AggregateException ex)
{
throw ex.InnerException;
}
I have too many methods that do it the same way. I was wondering if there is a way to avoid using try/catch block in each and every method. Maybe there is an exception filter mechanism, sorta like used on webApi to catch all exceptions within the class in one place? Maybe some attribute I can mark needed methods with?
First off I would recommend that you don't use .Result on a Task. See https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html or https://montemagno.com/c-sharp-developers-stop-calling-dot-result/.
If you follow the above advice and await a Task within a try block, it will throw the actual exception rather than an AggregateException so you might be able to avoid your rethrow code altogether.
Otherwise, if you really want to stick with your .Result code, you can write a generic wrapper method that does common error handling for you:
try
{
var task = Task.Run(async () => await this.Object(User));
return task.Result.Content;
}
catch(AggregateException ex)
{
throw ex.InnerException;
}
To something like:
return RunAsync(() => this.Object(User));
private T RunAsync<T>(Func<Task<T>> func)
{
try
{
var task = Task.Run(func);
return task.Result;
}
catch(AggregateException ex)
{
throw ex.InnerException;
}
}
EDIT:
I've just realised there's another way (see http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html) which is slightly more "hacky" since it feels more hidden but this:
var task = Task.Run(async () => await this.Object(User));
return task.GetAwaiter().GetResult().Content;
The .GetAwaiter().GetResult() will synchronously wait for the Task (as per .Result) but will not wrap any thrown exceptions in an AggregateException - which seems to be your desire.
I'm designing some code right now where I'm throwing an exception if a string parameter is null or empty and the exception is thrown as it should be, but it isn't getting caught when I'm UnitTesting.
Here's the client I'm using.
public class PipeClient : IPipeClient
{
public async void Send(string host, string pipeName, Message msg)
{
if (string.IsNullOrEmpty(msg.PreparedMessage))
throw new ArgumentException("MESSAGE_NOT_FOUND");
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(pipeName))
throw new ArgumentNullException();
if (!host.TryParseHost())
throw new ArgumentException("INVALID_HOST_NAME");
using (var pipeClient = new NamedPipeClientStream(host, pipeName, PipeDirection.Out))
{
pipeClient.Connect(200);
using (var writer = new StreamWriter(pipeClient))
{
await Task.Run(() => writer.WriteLine(msg.PreparedMessage));
writer.Flush();
}
}
}
}
And here's the UnitTest
[TestMethod]
public void Send_FailsOnWrongHostName()
{
var name = "FailWithHostname";
var msg = new Message(MyStates.Register, "UnitTest", "Test");
try
{
var client = new PipeClient();
client.Send("lol", name, msg);
}
catch (Exception e)
{
Assert.IsTrue(e is ArgumentException);
}
}
So when I run that test it should as far as I know throw the exception when I call the the Send method (which is does) and then get caught in the catch clause because I'm not catching it inside the PipeClient. Yet it doesn't, it just exits with a failed test.
If you need any more information just let me know, thanks in advance.
there's a few things I want to raise in this answer. I'm not sure of your experience level so please don't think I'm being condescending at any point.
Firstly a brief note on async methods and Tasks.
Async void should be avoided unless in an async event handler. Async methods should return Task or Task otherwise there is nothing for the calling method to keep hold of to know when the method is done and to report back whether the method threw an exception. Async void is essentially fire and forget, there is no one left to observe the exceptions.
"In observed Tasks no one can you scream" -Me ,2018
Exceptions thrown in async methods are nicely unwrapped and thrown
when the async method is awaited, with the call stack all preserved
and reasonably sensible. If you don't await the result eventually at
some point in the future you will get an UnobservedTaskException
that, if you haven't configured a global handler for, will bring down
your application. If you get the result of an async method
synchronously using .Wait() or .Result or via
.GetAwaiter().GetResult() (all 3 you should try and avoid but the 3rd
option is best if you have to I have been informed), then you will
get the original exception wrapped in an AggregateException.
Now if none of this is making much sense to you, I would recommend doing some reading up Tasks and async/await.
Now onto your Test.
Your method is async void so there is nothing for the calling method to have returned to it to represent the work or to let it know that the method has thrown an exception. So it carries on, the test finishes and then everything completes with no exceptions because the UnobservedTaskException can be thrown at anypoint in the future (I think it is related to when the garbage collector tidies up the faulted Task and then it throws and because the garbage collector is non-deterministic we can't say when that will happen)
So what if you made your async method return a Task??? Well that's still not quite right. You are now returning a Task that will be in a faulted state because of the exception, however because you never await it, the exception is never 'unwrapped' and actually thrown and so you're test happily continues.
What you need to do is make your Test async and return a Task and make the method you're testing async Task not async void and await that method in your test.
Like this
[TestMethod]
public async Task Send_FailsOnWrongHostName()
{
var name = "FailWithHostname";
var msg = new Message(MyStates.Register, "UnitTest", "Test");
try
{
var client = new PipeClient();
await client.Send("lol", name, msg);
}
catch (Exception e)
{
Assert.IsTrue(e is ArgumentException);
}
}
public class PipeClient : IPipeClient
{
public async Task Send(string host, string pipeName, Message msg)
{
if (string.IsNullOrEmpty(msg.PreparedMessage))
throw new ArgumentException("MESSAGE_NOT_FOUND");
if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(pipeName))
throw new ArgumentNullException();
if (!host.TryParseHost())
throw new ArgumentException("INVALID_HOST_NAME");
using (var pipeClient = new NamedPipeClientStream(host, pipeName, PipeDirection.Out))
{
pipeClient.Connect(200);
using (var writer = new StreamWriter(pipeClient))
{
await Task.Run(() => writer.WriteLine(msg.PreparedMessage));
writer.Flush();
}
}
}
}
I'm trying to understand some (in my eyes) weird behaviour. I have a call to some async method and want to retrieve its result. (DeleteIndexAsync return a Task<bool>)
var deleteTask = Task.Run(() => DeleteIndexAsync(localItem))
.ContinueWith(t =>
{
//handle and log exceptions
}, TaskContinuationOptions.OnlyOnFaulted);
if (!deleteTask.Result)
In this scenario Result is false and the Status is WaitingForActivation.
Whereas this code does what I want
var deleteTask = Task.Run(() => DeleteIndexAsync(localItem));
deleteTask.ContinueWith(t =>
{
//handle and log exceptions
return false;
}, TaskContinuationOptions.OnlyOnFaulted);
if (!deleteTask.Result)
Can someone explain why? And is it possible to use async / await instead of Task here?
Edit:
var deleteTask = Task.Run(() => ThrowEx());
bool errorOccurred = false;
deleteTask.ContinueWith(t =>
{
errorOccurred = true;
}, TaskContinuationOptions.OnlyOnFaulted);
if (errorOccurred)
{
return true;
}
If you chain the calls, like in the first example, the value you are assigning to the deleteTask variable is actually the second Task. This is the one that is supposed to run only on failure of the first task (the one calling DeleteIndexAsync).
This is because both Task.Run and Task.ContinueWith return Tasks that they create. It explains why in the first example you get Status == WaitingForActivation. In the first snippet, accessing deleteTask.Result would cause an exception to be thrown. In case DeleteIndexAsync threw, it would be an AggregateException containing original exception (unless you accessed t.Exception), otherwise it would be stating that the "Operation was cancelled" - this is because you try to get the result of the task that was scheduled conditionally and the condition was not met.
If you made method containing the snipped async you could do it like this (not tested):
bool success = false;
try
{
success = await DeleteIndexAsync(localItem);
}
catch (Exception) {}
if (!success)
{
//TODO: handler
}
Regarding question edit:
Using captured variable should help, but your current solution introduces race condition. In this case it would be better to do like this:
var deleteTask = Task.Run(() => ThrowEx());
try
{
deleteTask.Wait();
}
catch (Exception ex)
{
return true;
}
but at this point you can drop the asynchronous call entirely, because you wait for the result immediately - unless this example simplifies work that can be done between Run and Wait)
I have a call to some async method and want to retrieve its result.
The best way to do this is with await, not Result.
Can someone explain why?
In the first example, deleteTask is the task returned from Task.Run. In the second example, deleteTask is the task returned from ContinueWith. This task never actually executes unless DeleteIndexAsync throws an exception.
And is it possible to use async / await instead of Task here?
Yes, that's the best approach. When working with asynchronous code, you should always use await instead of ContinueWith. In this case, the TaskContinuationOptions.OnlyOnFaulted means you should put the continuation code in a catch block after the await:
bool deleteResult;
try
{
deleteResult = await Task.Run(() => DeleteIndexAsync(localItem));
}
catch (Exception ex)
{
//handle and log exceptions
}
if (!deleteResult)
...
Or, since it looks like DeleteIndexAsync is asynchronous, removing Task.Run would be more appropriate:
bool deleteResult;
try
{
deleteResult = await DeleteIndexAsync(localItem);
}
catch (Exception ex)
{
//handle and log exceptions
}
if (!deleteResult)
...
I have some kind of data processing, that is depnded on the success of problematic method that returns a result that used in my processing.
The external method is problematic since it works slowly, it may crash and throw exceptions of any type, and I don't have its source code.
I want to use thread in the beginning of my processing to save some time, since my processing is long enough even without that problematic method. but there exists a point that I must have a valid result from the problematic method that I cannot continue if it fails.
I want the use the exceptions of the problematic method in the main thread, so they get the same exception handling as any other exceptions that may be thrown by my processing.
Here is my code - it seems ok and it works, but it just looks too cumbersome to me, so this my question: Is there a better approach to manage correctly the call to the problematic method by thread, and its potential exceptions?
My environment is .NET 3.5, so please I would like to get answers focusing that version, but I would like also to learn if there are new approaches for newer .NET versions.
Thank you very much!
public void DoProcess()
{
object locker = new object();
bool problematicCodeFinished = false;
Exception methodException = null;
Result result;
Func<Result> getProblematicResult = new Func<Result>(() => problematicMethod()); //create delegate
//run problematic method it in thread
getProblematicResult.BeginInvoke((ar) => //callback
{
lock(locker)
{
try
{
result = getProblematicResult.EndInvoke();
}
catch (Exception ex)
{
methodException = ex;
}
finally
{
problematicCodeFinished = true;
Monitor.Pulse(locker);
}
}
}, null);
//do more processing in main thread
// ...
// ...
// ...
try
{
//here we must know what was the result of the problematic method
lock (locker)
{
if (!problematicCodeFinished) //wait for problematic method to finish
{
Monitor.Wait(locker);
}
}
//throw problematic method exception in main thread
if (methodException != null)
{
throw methodException;
}
//if we are here we can assume that we have a valid result, continue processing
// ...
// ...
// ...
}
catch (Exception ex) //for the problematic method exception, or any other exception
{
//normal exception handling for my processing
}
}
You're making life hard for yourself. Try using tasks from the TPL instead.
Your code would look like this:
public void DoProcess()
{
var task = Task.Factory.StartNew(() => problematicMethod());
//do more processing in main thread
// ...
// ...
// ...
var result = task.Result;
}
Update from the future: TL;DR to catch expressions in async methods you have to await, Task.WaitAll, or .Result.
I created a somewhat convoluted async method that just runs other async methods. You can disregard most of it as only the line var mSpekTask... is of interest, also, I don't care about the logic, I only want to know where my exception went. My main problem is that ex.ToString() is never hit even though inside mSpecTask an exception definitly happens.
public async Task LoadAsync(IEnumerable<ProductRequest> feed, int? customerId,
IProgress<int> mSpecProgress, Action<Task> mSpecCompletionHandler)
{
var ids = feed.Select(x => x.ProductId.ToString()).Distinct().ToList();
try
{
var mSpecTask = this.LoadMSpecAsync(mSpecProgress, ids);
}
catch (Exception ex)
{
ex.ToString();
}
}
Here is the code for LoadMSpecAsync
public Task<ResultSet> LoadMSpecAsync(IProgress<int> prg, IEnumerable<string> ids)
{
return this.LoadAsync(prg, ids, Selector.M, SPMS, x => x.Order);
}
Here is the code for LoadAsync, await db.ExecuteTVP(progress, spName, ids, parameters) generates an exception.
private async Task<Dictionary<Pair, dynamic>> LoadAsync(IProgress<int> progress,
IEnumerable<string> ids, Selector s, string spName, Func<dynamic, int> k,
Func<dynamic, dynamic> f = null, object parameters = null)
{
parameters = new ExpandoObject().CopyFromSafe(parameters);
if (spName != SPMAP) ((dynamic)parameters).lang = this.languageCode;
using (var db = new SqlConnection(this.connectionString))
{
await db.OpenAsync();
var results = await db.ExecuteTVP(progress, spName, ids, parameters);
db.Close();
}
return this.data[s];
}
When an async method throws an exception, that exception is placed on the returned Task. It's not raised directly to the caller. This is by design.
So, you have to either await the Task returned from LoadMSpecAsync or have your mSpecCompletionHandler examine its Task argument for exceptions. It will show up there.
You can handle unobserved Task exceptions as follows:
TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) =>
{
eventArgs.SetObserved();
((AggregateException)eventArgs.Exception).Handle(ex =>
{
//TODO: inspect type and handle exception
return true;
});
};
I'm going to add an answer to my own question because there's a useful piece of information that I found out. The intermediary method LoadMSpecAsync is swalloing the exception. For this not to happen it needs a little teak. You need to add the async keyword before the return type and the "await" keyword after "return".
I had an Exception being swallowed by myTask.Wait() (or WaitAsync), but it was a task-nesting issue. That is, when I did the myTask.Wait(), myTask had a passed-in parameter Task that ALSO called myParameterTask.Wait(). The parameter task had its own Exception-catch and no throw, so the originally thrown Exception wasn't propagating back to the calling thread.
I thought there was an issue with myTask.Wait() throwing Exceptions, but it wasn't.