Odd behaviour when in async await (maybe because of multiple requests) - c#

I have the following use-case, FetchDataService (injected as scoped), is fetching data from API using httpClient.
Then the service analyze the data and run await SaveAsync it to the database.
Everything till this point works great.
Then I added another service: AddressEnrichmentService (injected as scoped) that during the "analyze" phase is calling external service asyncly and bring more data.
This service is messing the entire app. I get random exceptions from the database handler (NHibernate) that is indicating some thread problems. I really can't put my finger on it. few different random exceptions...
These exceptions are thrown when running await SaveAsync. (I will add them in the end).
public async Task Fetch(string url, UserModel userModel ){
string res = null;
using (var httpClient = new HttpClient()){
res = await httpClient.GetStringAsync(url);
}
await databaseLogRepository.SaveAsync(new Log{Message = "The data is here..." + res});
var profile = JsonConvert.DeserializeObject<ProfileModel >(res);
profile.Data.Locations.ForEach(async x =>
{
var address = await addressEnrichmentService.EnrichAsync(x.StreetAddress + " " + x.Locality + " " + x.Country);
userModel.Address = address;
await databaseLogRepository.SaveAsync(new Log{Message = "enriched address"});
});
}
The problematic service:
public class AddressEnrichmentService : IAddressEnrichmentService
{
public async Task<AddressModel> EnrichAsync(string address)
{
string res = null;
using (HttpClient client = new HttpClient())
{
var url = "https://maps.googleapis.com/maps/api/geocode/json?key=__KEY__&address=" + HttpUtility.UrlEncode(address);
res = await client.GetStringAsync(url);// probably the problematic row
}
return JsonConvert.DeserializeObject<AddressModel>(res);
}
}
Usage
UserModel userModel = new UserModel();
await fetchDataService.Fetch(url, userModel);
await userRepo.SaveAsync(userModel);
Again addressEnrichmentService.EnrichAsync is messing everything up.
How do I know EnrichAsync is messing it up?
if I convert res = await client.GetStringAsync(url); to res = client.GetStringAsync(url).GetAwaiter().GetResult(); I dont get any error. 100 times of 100 trys I get no error. if I roll it back to res = await client.GetStringAsync(url); I get an error every time.
About the exceptions, again, im getting some indications that the error related to concurrency, this is the save method:
public async Task<T> SaveAsync(T entity)
{
using (var tr = session.BeginTransaction())
{
//session is injected as scoped
await session.SaveOrUpdateAsync(entity);
tr.Commit();
}
return entity;
}
MySqlException: There is already an open DataReader associated with this Connection which must be closed first.
NHibernate.HibernateException: 'Flush during cascade is dangerous'

Here is the problem:
profile.Data.Locations.ForEach(async x =>
The ForEach method accepts an Action, not an asynchronous delegate. So your async delegate does not produce an awaitable Task. It produces a void. The result is that you end up with an async void delegate, that has tortured myriads of developers before you (search for async void to see a never-ending list of related questions). Since the started asynchronous operations are not awaited (they are not awaitable), they are running concurrently. Your database is receiving multiple concurrent requests, and obviously can't handle them well. Not to mention that if any of these operations fail, the error will be thrown on the captured synchronization context, causing most likely the process to crash. You can't handle the error of an async void invocation, by wrapping the invocation in a try/catch block.
To solve the problem, just replace the fancy ForEach with a plain vanilla foreach. Each Task will be properly awaited, the unwanted parallelism will not occur, and the problem will be solved.
foreach (var x in profile.Data.Locations)
{
var address = await addressEnrichmentService.EnrichAsync(
x.StreetAddress + " " + x.Locality + " " + x.Country);
userModel.Address = address;
await databaseLogRepository.SaveAsync(new Log{Message = "enriched address"});
}

Related

Using return value from awaited method

I'm trying to understand async methods and await, I've got this simple example:
using (var client = new AmazonSQSClient())
{
var sqsRequest = new SendMessageRequest()
{
MessageBody = JsonConvert.SerializeObject(callProcessingRequest),
QueueUrl = "https://sqs.eu-west-2.amazonaws.com/*****6014/W*****g"
};
LoggingHelper.Log(LoggingLevel.INFO, "Calling SQS", context);
var sqsResponse = await client.SendMessageAsync(sqsRequest);
LoggingHelper.Log(LoggingLevel.DEBUG,
JsonConvert.SerializeObject(sqsResponse), context)
}
When I run this, the logging of sqsResponse never happens, however if I change
var sqsResponse = await client.SendMessageAsync(sqsRequest);
to
var sqsResponse = client.SendMessageAsync(sqsRequest).Result;
Then it works as expected.
With it being an async method I guess I should be using await with it, but not sure why it's not working.
EDIT: Whole method as requested. Adding .ConfigureAwait(false) didn't help.
public static async Task ProcessOutstandingDialplanItems()
{
var context = new Context() { CurrentHandler = "PBXCallbacksLambdaFunction" };
var callProcessingRequest = new CallProcessingRequest()
{
context = context,
MethodToInvoke = "ProcessOutstandingDialPlanItems"
};
try
{
using (var client = new AmazonSQSClient())
{
var sqsRequest = new SendMessageRequest()
{
MessageBody = JsonConvert.SerializeObject(callProcessingRequest),
QueueUrl = "https://sqs.eu-west-2.amazonaws.com/XXX6014/WXXXg"
};
LambdaLogger.Log("Calling SQS");
var sqsResponse = await client.SendMessageAsync(sqsRequest)
.ConfigureAwait(false);
//var sqsResponse = client.SendMessageAsync(sqsRequest).Result;
LambdaLogger.Log(JsonConvert.SerializeObject(sqsResponse));
}
}
catch (Exception x)
{
LambdaLogger.Log(x.Message);
}
}
From AWS Logging .NET:
AWS Lambda
These packages batch logging messages in a queue and send messages to CloudWatch Logs using a background thread. The use of the background thread means that the messages are not guaranteed to be delivered when used in AWS Lambda. The reason is because the background thread will be frozen once a Lambda event is processed and may not ever be unfrozen if more Lambda events are not received for some time.
When using Lambda it is recommended to use either the ILambdaContext.Logger.LogLine or the Amazon.Lambda.Logging.AspNetCore package.
My guess would be that you are already using Result or Wait (or something which is calling your code) in one of the method before in application which has SynchronizationContext (classic ASP.NET, UI apps like WPF, WinForms, etc.). That is a "good" way to end up in deadlock. One thing you can try - adding ConfigureAwait(false) to the call:
var sqsResponse = await client.SendMessageAsync(sqsRequest).ConfigureAwait(false);
But much better course of action - Don't Block on Async Code.

C# - HTTP Get run in parallel tasks randomly fails with: Error while copying content to stream

In my C# application (.NET Core 3.1) there's an automatic task that every X hours starts another task, which gets run in parallel multiple times with different parameters.
At the end of this automatic task, there's a call to await Task.WhenAll(tasksList). to wait for parallel tasks completion.
Every task issues an HTTPClient (using IHttpClientFactory factory method) and issues a GET request, with the following syntax:
var res = await client.GetAsync(url);
if (res.IsSuccessStatusCode)
{
var exit = await res.Content.ReadAsStringAsync();
[...omitted]
}
The issue occurs randomly when two tasks, that share the same GET URL, run at a distance of max 60-70ms. Sometimes both tasks fail, one after another, each with this same exception:
System.Net.Http.HttpRequestException: Error while copying content to a stream.
---> System.IO.IOException: The response ended prematurely.
at System.Net.Http.HttpConnection.FillAsync()
at System.Net.Http.HttpConnection.ChunkedEncodingReadStream.CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionResponseContent.SerializeToStreamAsync(Stream stream, TransportContext context, CancellationToken cancellationToken)
at System.Net.Http.HttpContent.LoadIntoBufferAsyncCore(Task serializeToStreamTask, MemoryStream tempBuffer)
From the logs, I can see how two different HTTP Requests are correctly started and received by the server.
If I remove the ReadAsStringAsync part, the issue never occurs so I presume it's related to the content reading (after the status code check), almost as if the two tasks end sharing the Get result (while having two different active connections issued). I tried using a ReadAsStreamAsync but the issue still occurs (this helps to reduce the occurrence, although).
Another thing that could be related is that the result retrieved is quite heavy (the last time I downloaded it, it ended being a .json file of 4.5MB, more or less).
Should I run each task sequentially? Or am I issuing the HTTP Requests wrong?
IF you want to test this issue, here you can find the source code of a console app I'm using to reproduce the issue (if it doesn't occurs by the first 20calls, restart the app until it occurs):
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task<int> Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHttpClient();
services.AddTransient<TaskRunner>();
}).UseConsoleLifetime();
var host = builder.Build();
using (var serviceScope = host.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
try
{
var x = services.GetRequiredService<TaskRunner>();
var result = await x.Run();
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine("Error Occured");
}
}
return 0;
}
public class TaskRunner
{
private static IHttpClientFactory _httpFactory { get; set; }
public TaskRunner(IHttpClientFactory httpFactory)
{
_httpFactory = httpFactory;
}
public async Task<string> Run()
{
Console.WriteLine("Starting loop...");
do
{
await Task.Delay(2500); // wait app loading
await SendRequest();
} while (true);
}
private static async Task SendRequest()
{
await Task.WhenAll(new Task[] { ExecuteCall(), ExecuteCall()};
}
private async static Task<bool> ExecuteCall()
{
try
{
var client = _httpFactory.CreateClient();
// fake heavy API call (> 5MB data)
var api = "https://api.npoint.io/5896085b486eed6483ce";
Console.WriteLine("Starting call at " + DateTime.Now.ToUniversalTime().ToString("o"));
var res = await client.GetAsync(api);
if (res.IsSuccessStatusCode)
{
var exit = await res.Content.ReadAsStringAsync();
/* STREAM read alternative
var ed = await res.Content.ReadAsStreamAsync();
StringBuilder result = new StringBuilder();
using var sr = new StreamReader(ed);
while (!sr.EndOfStream)
{
result.Append(await sr.ReadLineAsync());
}
var exit = result.ToString();
*/
Console.WriteLine(exit.Substring(0, 10));
//Console.WriteLine(exit);
Console.WriteLine("Ending call at " + DateTime.Now.ToUniversalTime().ToString("o"));
return true;
}
Console.WriteLine(res.StatusCode);
Console.WriteLine("Ending call at " + DateTime.Now.ToUniversalTime().ToString("o"));
return false;
}
catch (Exception ex)
{
// put breakpoint here
// Exception => called on line:78 but if content isn't read it never occurs
Console.WriteLine(ex.ToString());
return false;
}
}
}
}
}
Thanks for any help/suggestion you can give me!
I'm answering my question to leave the solution I applied, for anyone who can encounter the same issue :)
I added the following line before the Api Call:
var client = _httpFactory.CreateClient();
var api = "https://api.npoint.io/5896085b486eed6483ce";
>>> client.DefaultRequestVersion = HttpVersion.Version10; // new line
var res = await client.GetAsync(api);
The issue seems to be related to the endpoint server, that drops concurrent connections when the HttpVersion is 11. It's possible it relates to the Keep-Alive Connection header, since on 10 v. the header is set to Close.

Calling AWS RDS CreateDBSnapshotAsync Asynchronously "Set It And Forget It"

In an AWS Lambda function, I would like to be able to call a component to create a RDS DB Snapshot. There is an async method on the client named CreateDBSnapshotAsync. But, because this is AWS Lambda, I only have 5 minutes to complete the task. So, if I await it, the AWS Lambda function will timeout. And, apparently when it times out, the call is cancelled and then the snapshot is not completed.
Is there some way I can make the call in a COMPLETELY asynchronously way so that once I invoke it, it will complete no matter if my Lambda function times out or not?
In other words, I don't care about the result, I just want to invoke the process and move on, a "set it and forget it" mentality.
My call (without the await, obviously) is as below
using (var rdsClient = new AmazonRDSClient())
{
Task<CreateDBSnapshotResponse> response = rdsClient.CreateDBSnapshotAsync(new CreateDBSnapshotRequest($"MySnapShot", instanceId));
}
As requested, here's the full method:
public async Task<CloudFormationResponse> MigrateDatabase(CloudFormationRequest request, ILambdaContext context)
{
LambdaLogger.Log($"{nameof(MigrateDatabase)} invoked: " + JsonConvert.SerializeObject(request));
if (request.RequestType != "Delete")
{
try
{
var migrations = this.Context.Database.GetPendingMigrations().OrderBy(b=>b).ToList();
for (int i = 0; i < migrations.Count(); i++)
{
string thisMigration = migrations [i];
this.ApplyMigrationInternal(thisMigration);
}
this.TakeSnapshotAsync(context,migrations.Last());
return await CloudFormationResponse.CompleteCloudFormationResponse(null, request, context);
}
catch (Exception e)
{
LambdaLogger.Log(e.ToString());
if (e.InnerException != null) LambdaLogger.Log(e.InnerException.ToString());
return await CloudFormationResponse.CompleteCloudFormationResponse(e, request, context);
}
}
return await CloudFormationResponse.CompleteCloudFormationResponse(null, request, context);
}
internal void TakeSnapshotAsync(ILambdaContext context, string migration)
{
var instanceId = this.GetEnvironmentVariable(nameof(DBInstance));
using (var rdsClient = new AmazonRDSClient())
{
Task<CreateDBSnapshotResponse> response = rdsClient.CreateDBSnapshotAsync(new CreateDBSnapshotRequest($"{instanceId}{migration.Replace('_','-')}", instanceId));
while (context.RemainingTime > TimeSpan.FromSeconds(15))
{
Thread.Sleep(15000);
}
}
}
First refactor that sub function to use proper async syntax along with the use of Task.WhenAny.
internal async Task TakeSnapshotAsync(ILambdaContext context, string migration) {
var instanceId = this.GetEnvironmentVariable(nameof(DBInstance));
//don't wrap in using block or it will be disposed before you are done with it.
var rdsClient = new AmazonRDSClient();
var request = new CreateDBSnapshotRequest($"{instanceId}{migration.Replace('_','-')}", instanceId);
//don't await this long running task
Task<CreateDBSnapshotResponse> response = rdsClient.CreateDBSnapshotAsync(request);
Task delay = Task.Run(async () => {
while (context.RemainingTime > TimeSpan.FromSeconds(15)) {
await Task.Delay(15000); //Don't mix Thread.Sleep. use Task.Delay and await it.
}
}
// The call returns as soon as the first operation completes,
// even if the others are still running.
await Task.WhenAny(response, delay);
}
So if the RemainingTime runs out, it will break out of the call even if the snap shot task is still running so that the request does not time out.
Now you should be able to await the snapshot while there is still time available in the context
public async Task<CloudFormationResponse> MigrateDatabase(CloudFormationRequest request, ILambdaContext context) {
LambdaLogger.Log($"{nameof(MigrateDatabase)} invoked: " + JsonConvert.SerializeObject(request));
if (request.RequestType != "Delete") {
try {
var migrations = this.Context.Database.GetPendingMigrations().OrderBy(b=>b).ToList();
for (int i = 0; i < migrations.Count(); i++) {
string thisMigration = migrations [i];
this.ApplyMigrationInternal(thisMigration);
}
await this.TakeSnapshotAsync(context, migrations.Last());
return await CloudFormationResponse.CompleteCloudFormationResponse(null, request, context);
} catch (Exception e) {
LambdaLogger.Log(e.ToString());
if (e.InnerException != null) LambdaLogger.Log(e.InnerException.ToString());
return await CloudFormationResponse.CompleteCloudFormationResponse(e, request, context);
}
}
return await CloudFormationResponse.CompleteCloudFormationResponse(null, request, context);
}
This should also allow for any exceptions thrown by the RDS client to be caught by the currently executing thread. Which should help with troubleshooting any exception messages.
Some interesting information from documentation.
Using Async in C# Functions with AWS Lambda
If you know your Lambda function will require a long-running process, such as uploading large files to Amazon S3 or reading a large stream of records from DynamoDB, you can take advantage of the async/await pattern. When you use this signature, Lambda executes the function synchronously and waits for the function to return a response or for execution to time out.
From docs about timeouts
Function Settings
...
Timeout – The amount of time that Lambda allows a function to run before stopping it. The default is 3 seconds. The maximum allowed value is 900 seconds.
If getting a HTTP timeout then shorten the delay but leave the long running task. You still use the Task.WhenAny to give the long running task an opportunity to finish first even if that is not the expectation.
internal async Task TakeSnapshotAsync(ILambdaContext context, string migration) {
var instanceId = this.GetEnvironmentVariable(nameof(DBInstance));
//don't wrap in using block or it will be disposed before you are done with it.
var rdsClient = new AmazonRDSClient();
var request = new CreateDBSnapshotRequest($"{instanceId}{migration.Replace('_','-')}", instanceId);
//don't await this long running task
Task<CreateDBSnapshotResponse> response = rdsClient.CreateDBSnapshotAsync(request);
Task delay = Task.Delay(TimeSpan.FromSeconds(2.5));
// The call returns as soon as the first operation completes,
// even if the others are still running.
await Task.WhenAny(response, delay);
}

Gracefully handeling exceptions when dealing with WhenAll

I'm playing with a piece of code I wrote a while back. That piece of code deals with making a few requests in an async manner.
var client = new HttpClient();
var searchPromises = searchTerms
.Select(GetSearchUrl)
.Select(client.GetStringAsync);
var searchPages = await Task.WhenAll(searchPromises);
What happens is I create a new HttpClient. Using some search terch terms I compose search engine urls. Then I use those urls as inputs to get tasks representing the async requests for a page with the results. And last, I await those responses using Task.WhenAll to group them together.
The problem is if just one of those requests gets a 404, a 500 or anything like that my code throws an AggregateException.
Is there a way of specifying what should happen in the case of an error in one of those threads, so that I get a result from everything else?
I've looked at ContinueWith, but it doesn't seem to fit the bill, that is, it doesn't know how to deal with all the errors, just the aggregate one.
What happens is I create a new HttpClient. Using some search terch terms I compose search engine urls. Then I use those urls as inputs to get tasks representing the async requests for a page with the results. And last, I await those responses using Task.WhenAll to group them together.
Is there a way of specifying what should happen in the case of an error in one of those threads, so that I get a result from everything else?
IMO, the easiest solution is to change how you think about the problem. Right now, you're thinking "perform a download on each url" and then "what for them all to complete and handle errors on a per-item basis". Just change your operation ("download") to include anything you want to do per-item. In other words, what you want to do is "perform a download on each url and handle errors" and then "wait for them all to complete":
var client = new HttpClient();
var searchPromises = searchTerms
.Select(GetSearchUrl)
.Select(url => DownloadAsync(client, url));
var searchPages = await Task.WhenAll(searchPromises);
var successfulSearchPages = searchPages.Where(x => x != null);
...
private static async Task<string> DownloadAsync(HttpClient client, string url)
{
try
{
return await client.GetStringAsync(url);
}
catch (HttpRequestException ex)
{
// TODO: Perform appropriate error handling
return null;
}
}
Task.WhenAll will return a task that is completed when all the tasks passed as argument are completed.
If any of the tasks passed as argument ends in a Faulted state (an exception was thrown), the returned task will also end in a Faulted state and its Exception property will contain the aggregation of all exceptions thrown by the tasks passed as argument.
Because the code generated by the compiler picks the first exceptin on the list, only the excpetion thrown by the first exception that throws (not the first exception thrwing) will be rethrown.
But the tasks passed as argument still exist and can still be queried for result.
This code snippet shows this working:
var tasks = new Task[] {
((Func<Task>)(async () =>
{
await Task.Delay(10);
await Task.Delay(10);
await Task.Delay(10);
throw new Exception("First");
}))(),
((Func<Task>)(async () =>
{
await Task.Delay(10);
throw new Exception("Second");
}))(),
((Func<Task>)(async () =>
{
await Task.Delay(10);
}))()
};
var allTasks = Task.WhenAll(tasks);
try
{
await allTasks;
}
catch (Exception ex)
{
Console.WriteLine("Overall failed: {0}", ex.Message);
}
for(var i = 0; i < tasks.Length; i++)
{
try
{
await tasks[i];
Console.WriteLine("Taks {0} succeeded!", i);
}
catch (Exception ex)
{
Console.WriteLine("Taks {0} failed!", i);
}
}
/*
Overall failed: First
Taks 0 failed!
Taks 1 failed!
Taks 2 succeeded!
*/
You can create your own version of Task.WhenAll that returns just the results disregarding any exception using Task.WhenAny:
public static async Task<IEnumerable<TResult>> WhenAllSwallowExceptions<TResult>(IEnumerable<Task<TResult>> tasks)
{
var tasklist = tasks.ToList();
var results = new List<TResult>();
while (tasklist.Any())
{
var completedTask = await Task.WhenAny(tasklist);
try
{
results.Add(await completedTask);
}
catch (Exception e)
{
// handle
}
tasklist.Remove(completedTask);
}
return results;
}
Usage:
var searchPages = await WhenAllSwallowExceptions(searchPromises);
This waits for tasks one at a time (with Task.WhenAny) and aggregates all the results (if there are any).
I've found a way to do this, after many iterations. Tasks are starting to look like things that you need a library to abstract.
Anyway, here's the code:
var client = new HttpClient();
var exceptions = new ConcurrentBag<Exception>();
var searchPromises = searchTerms
.Select(GetSearchUrl)
.Select(client.GetStringAsync)
.Select(t=>t.Catch(e=>exceptions.Add(e)));
var searchPages = (await Task.WhenAll(searchPromises))
.Where(r => r != null);
And the implementation for Catch:
public static Task<TResult> Catch<TResult>(this Task<TResult> self, Action<Exception> exceptionHandlerTask)
{
return self.ContinueWith(s =>
{
if (!s.IsFaulted)
{
return s.Result;
}
exceptionHandlerTask(s.Exception);
return default(TResult);
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.DenyChildAttach,
TaskScheduler.Default);
}
What happens now is that it gives you a way to append a failure state function to the Task<T> promise. This allows me to still have chainability. It is a shame that c# doesn't have robust support for functional pattern matching to make this easier.
Edit: added minimal code for error logging.
Edit: separated the code for logging errors to be more generic/reusable.
Edit: separated the code for saving the errors from the Catch function.

C# await client.GetAsync() failing

I'm pulling data from Amazon via HTTP. The code works just fine in a small demo project, but in my main app it doesn't. When I call FetchItem() I receive this output:
'System.Net.Http.Formatting.dll'. Cannot find or open the PDB file.
After await client.GetAsync() the function returns and url.Wait() waits forever.
Usage
Task<string> url = FetchItem("ItemName", requestUrl);
url.Wait();
return url.Result;
Source of FetchItem
private static async Task<string> FetchItem(string sItemName, string url)
{
try
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
XElement content = await response.Content.ReadAsAsync<XElement>();
XNamespace ns = NAMESPACE;
var isValidResults = content.Descendants(ns + "IsValid").AsParallel();
foreach (var item in isValidResults)
{
if (item.Value != "True")
return "Invalid Request";
}
var titleResults = content.Descendants(ns + sItemName).AsParallel();
foreach (var item in titleResults)
{
if (item.Name == ns + sItemName)
return item.Value;
// depending on the keyword, results can be quite fun.... :-)
}
}
catch (Exception e)
{
System.Console.WriteLine("Caught Exception: " + e.Message);
System.Console.WriteLine("Stack Trace: " + e.StackTrace);
}
return "Error";
}
I assume you're calling this code on the UI thread. What's happening is that Waiting for the task to complete causes a deadlock. Your FetchItem method is asynchronous, and when you use await in it, the code that follows is transformed to a callback (called the continuation) that will be executed on the UI thread. But since the UI thread is busy waiting for the task to complete, it can't process the callback, so the task never completes. Hence the deadlock.
You should never Wait on the result of an async method. If you call an async method, use await to get its result. It means that the calling method also has to be async. Basically, when you start to use async in some part of the code, all the code that uses it needs to become async as well... (*)
(*) well, that's not exactly true. You can prevent the async method from resuming on the UI thread, by calling ConfigureAwait(false) on the method you await. If you do that, the continuation will run on a thread pool thread, instead of the UI thread. This will also avoid the deadlock.
I changed FetchItem() to run sync. That did it for me:
private static string GetItem(string sItemName, string url)
{
try
{
HttpClient client = new HttpClient();
HttpResponseMessage response = client.GetAsync(url).Result;
response.EnsureSuccessStatusCode();
XElement content = response.Content.ReadAsAsync<XElement>().Result;
...
It doesn't make sense to me to rewrite half my app to go async.

Categories