Async/await and WebException handling - c#

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);
}
}

Related

Exception handling on console application async code

Hoping that someone will be able to point me in the right direction. I have a console application that uses async method calls and I'm having a bit of trouble with the try/catch error blocks. Basically, when a catch is triggered it executes the logging code within the catch but it still throws the error up to the top level and terminates the overall execution of the app.
I've been looking through other queries about this and from what I've seen this type of behaviour can be caused by either an async void or an async Task with no await. Looking at my code I don't think I have either so I'm a bit stumped.
The error being introduced is a missing connection string (in the context file, not shown), which should throw a handled exception in the PayloadRepo method. Stepping through the debugger this error is indeed caught and logged as expected, however it still seems to bubble back up to the Main method and cause the overall execution to break.
Can anyone spot what may be happening?
Thank you!
static async Task Main(string[] args)
{
BuildLogConfiguration();
BuildDependencyContainer();
await RunProgram();
}
static async Task RunProgram()
{
systemLog.Info("Started full process at: " + System.DateTime.Now);
using (var scope = Container.BeginLifetimeScope())
{
var payloadService = scope.Resolve<IPayloadService>();
await payloadService.ProcessPayloadData(scope);
}
systemLog.Info("Completing full process at: " + System.DateTime.Now);
}
public class PayloadService : IPayloadService
{
private static readonly ILog _systemLog = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public async Task ProcessPayloadData(ILifetimeScope scope)
{
await PayloadData_Process(scope);
}
private async Task PayloadData_Process(ILifetimeScope scope)
{
var repo = scope.Resolve<IPayloadRepo>();
var payloadList = await repo.Payload_Get();
}
}
public class PayloadRepo : IPayloadRepo
{
public async Task<IEnumerable<Payload>> Payload_Get()
{
using (var context = new ASGB_DataLayerContext())
{
try
{
var payloadList = context.Payloads;
var result = await payloadList.ToListAsync();
return result;
}
catch (Exception ex) <<--- CATCHES THIS AND EXECUTES CODE WITHIN, BUT IT STILL THROWS AN ERROR ON RunProgram().Wait() AND TERMINATES PROGRAM
{
systemLog.Error(ex.Message);
systemLog.Error(ex.InnerException);
var entries = context.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged);
foreach (var entry in entries)
{
foreach (var prop in entry.CurrentValues.Properties)
{
var val = prop.PropertyInfo.GetValue(entry.Entity);
systemLog.Error($"{prop.ToString()} ~ ({val?.ToString().Length})({val})");
}
}
systemLog.Error("------------------------------------------------------------------------");
return null;
}
}
}
}

client.GetStreamAsync(url) is freezing my UI

So I'm listening to an Server side event with my code to just write it on the console (for now) but it seems that this is making my window's form UI freeze
The code in question (which I'm calling from the main form's function)
static async Task hello()
{
HttpClient client = new HttpClient();
//client.Timeout = TimeSpan.FromSeconds(5);
while (true)
{
try
{
Console.WriteLine("Establishing connection");
using (var streamReader = new StreamReader(await client.GetStreamAsync(url)))
{
while (!streamReader.EndOfStream)
{
var message = await streamReader.ReadLineAsync();
Console.WriteLine(message.ToString());
}
}
}
catch (Exception ex)
{
//Here you can check for
//specific types of errors before continuing
//Since this is a simple example, i'm always going to retry
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine("Retrying in 5 seconds");
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
}
Thanks in advance
I've solved the problem, it appears that async/await task freezes the GUI. To stop this from happening you need to use Task.Run(() => your_function()); when you call an async function
This question might be a possible duplicate of: GUI freezes when using async/await ... so go there if you want to find a bit more knowledge about the subject

C#: Crawl a page with asynchronous method

I'm coding a plug-in for Excel. I'd like to add a new method to excel that can crawl a web page and get back the html code.
my problem is that i have a lot of URLs to proces and if I use a sync method, it will take a lot of time and freeze my excel.
let say, i have a cell A1 which contains "http://www.google.com", and in A2, my method "=downloadHtml(A1)".
I'm using HttpClient because it is already handling Async. So here is my code :
static void Main()
{
GetWebPage(new Uri("http://www.google.com"));
}
static async void GetWebPage(Uri URI)
{
string html = await HttpGetAsync(URI);
//Do other operations with html code
Console.WriteLine(html);
}
static async Task<string> HttpGetAsync(Uri URI)
{
try
{
HttpClient hc = new HttpClient();
Task<Stream> result = hc.GetStreamAsync(URI);
Stream vs = await result;
StreamReader am = new StreamReader(vs);
return await am.ReadToEndAsync();
}
catch (WebException ex)
{
switch (ex.Status)
{
case WebExceptionStatus.NameResolutionFailure:
Console.WriteLine("domain_not_found");
break;
//Catch other exceptions here
}
}
return "";
}
The probem is that, when i run the program, the program exits before the task complete.
If i add a
Console.ReadLine();
the program will not exit do to the readline instruction, and after a couple of seconds, i see the html printed into my screen (du to the console.writeline instruction). So the program works.
how can i handle this ?
GetWebPage is a fire-and-forget method (an async void), so you cannot wait for it to finish.
You should be using this instead:
static void Main()
{
string html = Task.Run(() => HttpGetAsync(new Uri("http://www.google.com"))).GetAwaiter().GetResult();
//Do other operations with html code
Console.WriteLine(html);
}
Also, you could simplify the download code to this:
using (var HttpClient hc = new HttpClient())
{
return await hc.GetStringAsync(URI);
}

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();

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

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).

Categories