Exception not handled in caller module with async and anonymous methods [closed] - c#

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
i'm testing my class librabry that makes async post to a webserver.
Since the data to send require different manipolation, i insert the method to process and send them inside a blockingcollection. A task that run forever extract each method from the collection and execute it.
The problem is that if the post fails, the error doesn't bubble to the wpf caller module.
Here's the library module
private Task queueInvio;
private BlockingCollection<Action> codaInvio = null;
public MotoreClient()
{
codaInvio = new BlockingCollection<Action>();
queueInvio = Task.Factory.StartNew(() =>
{
while (true)
{
Action azione = null;
if (codaInvio.TryTake(out azione))
{
try
{
azione();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
}
}
, CancellationToken.None
, TaskCreationOptions.LongRunning
, TaskScheduler.Default);
}
This is the method called by the wpf test program
public void InviaAggiornamento(TipoAggiornamento tipoAggiornamento)
{
string nomePaginaApi = String.Empty;
HttpContent contenuto = null;
switch (tipoAggiornamento)
{
// blah blah code
}
// exception capture here, but not rethrown to the wpf module
codaInvio.Add(async () =>
{
try
{
await InviaAggiornamento(nomePaginaApi, contenuto);
}
catch (Exception ex)
{
throw;
}
});
}
This is the method that make the async post
private async Task InviaAggiornamento(string nomePaginaApi, HttpContent contenuto)
{
HttpClient httpClient = new HttpClient();
string riposta = String.Empty;
if (!string.IsNullOrEmpty(indirizzoServer))
{
try
{
httpClient.BaseAddress = new Uri(IndirizzoServer);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", USERNAME, PASSWORD))));
var response = await httpClient.PostAsync("api/liveTimingApi/" + nomePaginaApi, contenuto);
if (response.StatusCode != HttpStatusCode.NoContent)
throw new Exception("Richiesta PostAsync fallita.");
if (!response.IsSuccessStatusCode)
{
string rispostaErrore = string.Empty;
if (!string.IsNullOrEmpty(response.ReasonPhrase))
rispostaErrore += " ReasonPhrase: " + response.ReasonPhrase;
if (!string.IsNullOrEmpty(response.Content.ReadAsStringAsync().Result))
rispostaErrore += " Result: " + response.Content.ReadAsStringAsync().Result;
throw new ServerException(rispostaErrore.Trim());
}
}
catch (HttpRequestException hre)
{
throw new Exception("HttpRequestException: " + hre.Message);
}
catch (TaskCanceledException)
{
throw new Exception("Richiesta cancellata (TaskCanceledException).");
}
catch (Exception ex)
{
throw new Exception("Exception: " + ex.Message);
}
finally
{
if (httpClient != null)
{
httpClient.Dispose();
httpClient = null;
}
}
}
}
This is wpf module that simulate the send of the data
private void btnSendTest_Click(object sender, RoutedEventArgs e)
{
motoreClient.IndirizzoServer = "http://localhost:721";
motoreClient.AggiungiRigaProgrammaOrario(1, 1, "GaraDescrizione", DateTime.Now, "XXX", "SessioneDescrizione", "1:00:00", true);
try
{
motoreClient.InviaAggiornamento(TipoAggiornamento.ProgrammaOrario);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Since the IndirizzoServer (server address) is fake i have an HttpRequest exception.
I capture it inside the codaInvio.Add try/catch block, but I'm not able to rethrow it to the caller wpf module.
Visual studio says that the exception is not handled by the caller code.
Why? I'm using try/catch all the relevant piece of code.
If i'm not clear please let me know.
Marco

Your core problem is here:
BlockingCollection<Action>
Action is a void-returning delegate type, so when you pass an async lambda to Add, it is creating an async void method. There are several reasons to avoid async void; one is that it's not possible to catch exceptions from them using try/catch.
You could change the delegate type to be compatible with async Task, i.e., BlockingCollection<Func<Task>>, assuming that all the delegates passed to Add are async. This would require your "run forever" task to await the result, making its delegate also async. And then you'd need to change from Task.Factory.StartNew to Task.Run because StartNew doesn't understand async delegates.
But really, I'd recommend a much simpler solution: use an ActionBlock from TPL Dataflow (available via NuGet).

Related

ContinueWith not waiting for task to complete

I have a function (below) that I retrieve data from an API. If I set a breakpoint at the line that deserializes it, then I can see that it is populated with data which is great.
When I continue on, it goes into the second function (below) and it throws an error. The error says next to it Not yet computed, and therefore throwing an exception.
When I do it with a small list it works just fine (I presume its cos it's a small set of data).
How is this possible when I'm using ContinueWith (waiting for the task to complete)?
public static async Task<Data> GetAllCardsInSet(string setName)
{
setName = WebUtility.UrlEncode(setName);
var correctUri = Path.Combine(ApiConstants.YugiohGetAllCardsInSet, setName);
Console.WriteLine();
using (var httpClient = new HttpClient())
{
var response =
await httpClient.GetAsync(correctUri);
var result = await response.Content.ReadAsStringAsync();
var cardData = JsonConvert.DeserializeObject<CardSetCards>(result);
for (int i = 0; i < cardData.Data.Cards.Count; i++)
{
cardData.Data.Cards[i] = FormatWords(cardData.Data.Cards[i]);
}
return cardData.Data;
}
}
private void GetYugiohCardsAndNavigate(string name)
{
var cardSetData = YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name).ContinueWith((result) =>
{
//var cards = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
try
{
this.mainPage.NavigateToYugiohCardListPage(result.Result);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App", "Sorry, we could not fetch this set");
}
});
}
Your GetAllCardsInSet method no need to change.
But using of this method can be refactored.
Method GetAllCardsInSet return Task and you not observed the completion of the this Task.
You need to check is Task completes succesfully, easiest approach to use await keyword. Awaiting task will unwrapp returned value or throw exception if task completed with exception.
For using async/await in the GetYugiohCardsAndNavigate change method signature to aynchronous and returning Task
private async Task GetYugiohCardsAndNavigate(string name)
{
try
{
var cardSetData = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
this.mainPage.NavigateToYugiohCardListPage(cardSetData);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App",
"Sorry, we could not fetch this set");
}
}
you called an async method in a sync method without Wait. It should have been done like:
YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name).ContinueWith((result) =>
{
//var cards = await YugiohRequester.GetAllCardsInSet(_selectedCardSet.Name);
try
{
this.mainPage.NavigateToYugiohCardListPage(result.Result);
}
catch (Exception e)
{
HelperFunctions.ShowToastNotification("Trading Card App", "Sorry, we could not fetch this set");
}
}).Wait();

Catching exceptions in async code called synchronously

I have thrift service for authentication. catch (AccountNotFoundException) doesn't catch the exception unless I call it in Task.Run. The strange thing is that test case is fine. Why? Is it because task.start() is on the different level than catch?
public override string GetUserNameByEmail(string email)
{
var task = client.GetUserByEmail(email, false);
return task.Result;
// I changed to
// return Task.Run(() => client.GetUserByEmail(email, false)).Result.UserName;
// and I was able to catch the exception
}
public async Task<AccountDetails> GetAccountDetailsByEmail(string email)
{
try
{
return await Call(() => client.getAccountDetailsByEmail(email));
}
catch (AccountNotFoundException)
{
return null;
}
}
private async Task<T> Call<T>(Func<T> call)
{
try
{
transport.Open();
var thriftTask = new Task<T>(call);
thriftTask.Start();
return await thriftTask;
}
catch (DatabaseException e)
{
Logger.Error(e);
throw;
}
finally
{
transport.Close();
}
}
Test case works just fine
[TestMethod]
public async Task Nonexisting_User_I_Expect_To_Be_Null()
{
var user = Provider.GetUser("idontexist#bar.com", false);
Assert.IsNull(user);
}
EDIT:
I have a following theory why my code run ok: The code was working because I was lucky. Request and async was handled by the same thread so it shared the same context so it didn't block.
First, you shouldn't be calling asynchronous methods synchronously. As I describe on my blog, the approach you're using is prone to deadlocks.
The reason you're seeing an unexpected exception type is because Result will wrap any task exceptions in an AggregateException. To avoid this, you can call GetAwaiter().GetResult().
This doesn't have anything to do with Start, but since you mention it, the Start member doesn't really have a use case. There's never a good reason to use it. Instead, use Task.Run:
var thriftTask = Task.Run(call);
See here for details of exception handling for async code. It may be that you're catching an AccountNotFoundException, when you really want to be catching an Exception, which will have an InnerException set to the AccountNotFoundException:
https://msdn.microsoft.com/en-us/library/0yd65esw.aspx
An excerpt:
The task's IsFaulted property is set to True, the task's Exception.InnerException property is set to the exception, and the exception is caught in the catch block.
public async Task DoSomethingAsync()
{
Task<string> theTask = DelayAsync();
try
{
string result = await theTask;
Debug.WriteLine("Result: " + result);
}
catch (Exception ex)
{
Debug.WriteLine("Exception Message: " + ex.Message);
}
Debug.WriteLine("Task IsCanceled: " + theTask.IsCanceled);
Debug.WriteLine("Task IsFaulted: " + theTask.IsFaulted);
if (theTask.Exception != null)
{
Debug.WriteLine("Task Exception Message: "
+ theTask.Exception.Message);
Debug.WriteLine("Task Inner Exception Message: "
+ theTask.Exception.InnerException.Message);
}
}
private async Task<string> DelayAsync()
{
await Task.Delay(100);
// Uncomment each of the following lines to
// demonstrate exception handling.
//throw new OperationCanceledException("canceled");
//throw new Exception("Something happened.");
return "Done";
}

Good pattern for exception handling when using async calls

I want to consume an Web API and I see many people recommending System.Net.Http.HttpClient.
That's fine... but I have only VS-2010, so I cannot use async/await just yet. Instead, I guess I could use Task<TResult> in combination to ContinueWith. So I tried this piece of code:
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
client.GetStringAsync(STR_URL_SERVER_API_USERS).ContinueWith(task =>
{
var usersResultString = task.Result;
lbUsers.DataSource = JsonConvert.DeserializeObject<List<string>>(usersResultString);
});
My first observation was to realize that it doesn't generate any error if URL is not available, but maybe there will be more errors like this...
So I am trying to find a way to handle exceptions for such async calls (particularly for HttpClient). I noticed that "Task" has IsFaulted property and an AggregateException which maybe could be used, but I am not sure yet how.
Another observation was that GetStringAsync returns Task<string>, but GetAsync returns Task<HttpResponseMessage>. The latter could be maybe more useful, since it presents a StatusCode.
Could you share a pattern on how to use the async calls correctly and handle exceptions in a good way? Basic explanation would be appreciated as well.
I would not use a separate ContinueWith continuation for successful and faulted scenarios. I'd rather handle both cases in a single place, using try/catch:
task.ContinueWith(t =>
{
try
{
// this would re-throw an exception from task, if any
var result = t.Result;
// process result
lbUsers.DataSource = JsonConvert.DeserializeObject<List<string>>(result);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
lbUsers.Clear();
lbUsers.Items.Add("Error loading users!");
}
},
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()
);
If t is a non-generic Task (rather than a Task<TResult>), you can do t.GetAwaiter().GetResult() to re-throw the original exception inside the ContinueWith lambda; t.Wait() would work too. Be prepared to handle AggregatedException, you can get to the inner exception with something like this:
catch (Exception ex)
{
while (ex is AggregatedException && ex.InnerException != null)
ex = ex.InnerException;
MessageBox.Show(ex.Message);
}
If you're dealing with a series of ContinueWith, usually you don't have to handle exceptions inside each ContinueWith. Do it once for the outermost resulting task, e.g.:
void GetThreePagesV1()
{
var httpClient = new HttpClient();
var finalTask = httpClient.GetStringAsync("http://example.com")
.ContinueWith((task1) =>
{
var page1 = task1.Result;
return httpClient.GetStringAsync("http://example.net")
.ContinueWith((task2) =>
{
var page2 = task2.Result;
return httpClient.GetStringAsync("http://example.org")
.ContinueWith((task3) =>
{
var page3 = task3.Result;
return page1 + page2 + page3;
}, TaskContinuationOptions.ExecuteSynchronously);
}, TaskContinuationOptions.ExecuteSynchronously).Unwrap();
}, TaskContinuationOptions.ExecuteSynchronously).Unwrap()
.ContinueWith((resultTask) =>
{
httpClient.Dispose();
string result = resultTask.Result;
try
{
MessageBox.Show(result);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
},
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
Any exceptions thrown inside inner tasks will propagate to the outermost ContinueWith lambda as you're accessing the results of the inner tasks (taskN.Result).
This code is functional, but it's also ugly and non-readable. JavaScript developers call it The Callback Pyramid of Doom. They have Promises to deal with it. C# developers have async/await, which you're unfortunately not able to use because of the VS2010 restrain.
IMO, the closest thing to the JavaScript Promises in TPL is Stephen Toub's Then pattern. And the closest thing to async/await in C# 4.0 is his Iterate pattern from the same blog post, which uses the C# yield feature.
Using the Iterate pattern, the above code could be rewritten in a more readable way. Note that inside GetThreePagesHelper you can use all the familiar synchronous code statements like using, for, while, try/catch etc. It is however important to understand the asynchronous code flow of this pattern:
void GetThreePagesV2()
{
Iterate(GetThreePagesHelper()).ContinueWith((iteratorTask) =>
{
try
{
var lastTask = (Task<string>)iteratorTask.Result;
var result = lastTask.Result;
MessageBox.Show(result);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
throw;
}
},
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
IEnumerable<Task> GetThreePagesHelper()
{
// now you can use "foreach", "using" etc
using (var httpClient = new HttpClient())
{
var task1 = httpClient.GetStringAsync("http://example.com");
yield return task1;
var page1 = task1.Result;
var task2 = httpClient.GetStringAsync("http://example.net");
yield return task2;
var page2 = task2.Result;
var task3 = httpClient.GetStringAsync("http://example.org");
yield return task3;
var page3 = task3.Result;
yield return Task.Delay(1000);
var resultTcs = new TaskCompletionSource<string>();
resultTcs.SetResult(page1 + page1 + page3);
yield return resultTcs.Task;
}
}
/// <summary>
/// A slightly modified version of Iterate from
/// http://blogs.msdn.com/b/pfxteam/archive/2010/11/21/10094564.aspx
/// </summary>
public static Task<Task> Iterate(IEnumerable<Task> asyncIterator)
{
if (asyncIterator == null)
throw new ArgumentNullException("asyncIterator");
var enumerator = asyncIterator.GetEnumerator();
if (enumerator == null)
throw new InvalidOperationException("asyncIterator.GetEnumerator");
var tcs = new TaskCompletionSource<Task>();
Action<Task> nextStep = null;
nextStep = (previousTask) =>
{
if (previousTask != null && previousTask.Exception != null)
tcs.SetException(previousTask.Exception);
if (enumerator.MoveNext())
{
enumerator.Current.ContinueWith(nextStep,
TaskContinuationOptions.ExecuteSynchronously);
}
else
{
tcs.SetResult(previousTask);
}
};
nextStep(null);
return tcs.Task;
}

Async/await and WebException handling

I am struggling around the (seems so) pretty famous problem of the exception handling by using the async/await pattern. Specifically my context is on a HTTP client, but I have also tried with a much simpler test, and it behaves the same.
Consider the below program, which is a super-simplified version of my original app's context.
class Program
{
static void Main(string[] args)
{
Test();
Console.Write("Press any key...");
Console.ReadKey();
Console.WriteLine();
}
static async void Test()
{
var c = new MyClient();
try
{
var uri = new Uri("http://www.google.com/"); //valid address
var s = await c.GetString(uri);
Console.WriteLine(s.Length);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
try
{
var uri = new Uri("http://www.foo.bah/"); //non-existent address
var s = await c.GetString(uri);
Console.WriteLine(s.Length);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
}
}
class MyClient
{
public async Task<string> GetString(Uri uri)
{
var client = new HttpClient();
return await client.GetStringAsync(uri);
}
}
When the program starts, it downloads the first web site's page as a string, then displays its length: that's fine. Afterward, when the same operation is performed against an invalid address, the client raises a WebException (that's what I want), but it's not caught.
UPDATE: as "not caught", I mean that the code actually does not flow through the "catch" branch and silently displays the exception message. Instead, the exception is shown by the VS IDE, and the debugging breaks.
Any decent solution to catch the exception?
Many thanks in advance.
Although you have already figured out the exception is HttpRequestException not WebException, still I would like to highlight few important things about async-await operator usage.
async void is of type fire & forget and is only & only for event handlers.
As soon as compiler reaches first await operator inside async method control returns to the caller.
Debugging your code :-
Since you are using async void in Test method so the control returns to the caller and execution continues to line Console.Write("Press any key..."); without having any information about the Task and then you are waiting for the user input.
In the meanwhile response from awaited method comes and the execution continues inside Test method.
If you comment out the line Console.ReadKey(); inside main() OR user provides input immediately then you'll notice that response may or may not get printed. This is because you are not waiting on the Task getting executed you simply trusted on the user that he will not enter anything till your Task completes.
Solution:-
Solution is to return Task from Test() and then wait till it finishes, below is the updated code also note adding Async at the end of method name is the naming convention you must follow to save you from the headache of distinguishing between asynchronous and synchronous methods.
class Program
{
static void Main(string[] args)
{
Task task = TestAsync();
Console.Write("Press any key...");
task.wait();
//Console.ReadKey();
Console.WriteLine();
}
static async Task<string> TestAsync()
{
var c = new MyClient();
try
{
var uri = new Uri("http://www.google.com/"); //valid address
var s = await c.GetStringAsync(uri);
Console.WriteLine(s.Length);
}
catch (HttpRequestException ex)
{
Console.WriteLine(ex.Message);
}
try
{
var uri = new Uri("http://www.foo.bah/"); //non-existent address
var s = await c.GetStringAsync(uri);
Console.WriteLine(s.Length);
}
catch (HttpRequestException ex)
{
Console.WriteLine(ex.Message);
}
//to avoid compiler error
return null;
}
}
class MyClient
{
public async Task<string> GetStringAsync(Uri uri)
{
var client = new HttpClient();
return await client.GetStringAsync(uri);
}
}

Where is WebClient.DownloadStringTaskAsync(Uri,CancellationToken) in VS11

In the Async CTP there is an extension method with the signature
WebClient.DownloadStringTaskAsync(Uri,CancellationToken)
Where is this in VS11?
Do I need to install the Async CTP to get this method?
In .NET 4.5, you would probably use the new HttpClient Class, in particular the GetStringAsync Method.
It's unfortunate that CancellationToken support isn't built in, but here's how you can approximate it by leveraging the Register and CancelAsync methods:
var downloadTask = webClient.DownloadStringTaskAsync(source);
string text;
using (cancellationToken.Register(() => webClient.CancelAsync()))
{
text = await downloadTask;
}
It's still there in .Net 4.5 beta, see MSDN, except it's not an extension method anymore.
What you may be referring to is the fact that WebClient is not included in .Net for Metro-style apps. There, you should probably use HttpClient. Another option is to use HttpWebRequest, which is still present and has been extended with Task-based async methods as well.
Both classes System.Net.WebClient and System.Net.Http.HttpClient have an async function. This enables you to create an async function. While the GetStringAsync function is running asynchronously you can check regularly if cancellation is requested.
Example:
using System.Net.Http;
class HttpSonnetFetcher
{
const string sonnetsShakespeare = #"http://www.gutenberg.org/cache/epub/1041/pg1041.txt";
public async Task<IEnumerable<string>> Fetch(CancellationToken token)
{
string bookShakespeareSonnets = null;
using (var downloader = new HttpClient())
{
var downloadTask = downloader.GetStringAsync(sonnetsShakespeare);
// wait until downloadTask finished, but regularly check if cancellation requested:
while (!downloadTask.Wait(TimeSpan.FromSeconds(0.2)))
{
token.ThrowIfCancellationRequested();
}
// if still here: downloadTask completed
bookShakespeareSonnets = downloadTask.Result;
}
// just for fun: find a nice sonnet, remove the beginning, split into lines and return 12 lines
var indexNiceSonnet = bookShakespeareSonnets.IndexOf("Shall I compare thee to a summer's day?");
return bookShakespeareSonnets.Remove(0, indexNiceSonnet)
.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Take(12);
}
}
Usage will be as follows:
private void TestCancellationHttpClient()
{
try
{
var sonnetFetcher = new HttpSonnetFetcher();
var cancellationTokenSource = new CancellationTokenSource();
var sonnetTask = Task.Run(() => sonnetFetcher.Fetch(cancellationTokenSource.Token));
cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));
// meanwhile do something else, checking regularly if the task finished, or if you have nothing to do, just Task.Wait():
while (!sonnetTask.Wait(TimeSpan.FromSeconds(0.25)))
{
Console.Write('.');
}
// if still here: the sonnet is fetched. return value is in sonnetTask.Result
Console.WriteLine("A nice sonnet by William Shakespeare:");
foreach (var line in sonnetTask.Result)
{
Console.WriteLine(line);
}
}
catch (OperationCanceledException exc)
{
Console.WriteLine("Canceled " + exc.Message);
}
catch (AggregateException exc)
{
Console.WriteLine("Task reports exceptions");
var x = exc.Flatten();
foreach (var innerException in x.InnerExceptions)
{
Console.WriteLine(innerException.Message);
}
}
catch (Exception exc)
{
Console.WriteLine("Exception: " + exc.Message);
}
}
Try this in a simple console program, and see that the sonnet is fetched properly, Decrease CancelAfter from 10 seconds until, say 0.1 second, and see that the task is properly cancelled.
Nota Bene: Although an OperationCancelledException is thrown, this exception is wrapped as an inner exception of an AggregateException. All exceptions that occur within a task are always wrapped in an AggregateException.

Categories