Search code examples
c#httpclientasp.net-web-apiasync-await

http client frozes in windows form app


I am following this example, and it works in a console application, but then I tried in a windows form app and it forzes, when in hits the line await client.GetAsync("api/branches/1035") how is it diferent?

console code (this works):

static void Main()
    {
        RunAsync().Wait();
    }

    static async Task RunAsync()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://localhost:49358/");

           client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            HttpResponseMessage response = await client.GetAsync("api/branches/1035");

            if (response.IsSuccessStatusCode)
            {
                branch branch = await response.Content.ReadAsAsync<branch>();
                Console.WriteLine("{0}\t${1}", branch.Id, branch.Color);
            }
        }
    }

and this is frozen when it hits await client.GetAsync("api/branches/1035")

private void button1_Click(object sender, EventArgs e)
    {
        RunAsync().Wait();
    }

    static async Task RunAsync()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://localhost:49358/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            HttpResponseMessage response = await client.GetAsync("api/branches/1035");

            if (response.IsSuccessStatusCode)
            {
                Branch branch = await response.Content.ReadAsAsync<Branch>();
                Console.WriteLine("{0}\t${1}", branch.Id, branch.Color);
            }

        }
    }

Solution

  • You're seeing a deadlock that I explain fully on my blog. Essentially, an await will capture a "context" and use that to resume the async method. In the Console app, this "context" is the thread pool context, but in the UI app, this "context" is the UI thread context.

    Further up the call stack, you're calling Wait, which blocks that thread until the task completes. In the Console app, the async method resumes on a thread pool thread; but in the UI app, the async method cannot resume on the UI thread (because the UI thread is blocked in the call to Wait).

    To fix this, use async all the way:

    private async void button1_Click(object sender, EventArgs e)
    {
      await RunAsync();
    }