I'm using HttpClient
and ProgressMessageHandler
from the MS ASP.NET Web API Client Libraries.
I've happily tinkered with this in a console application without issue, but now in a WinForm app, a "Post" task just gets plain old stuck on either .Wait()
or .Result
.
Below is a complete listing of my very simple test application. Button 1 works fine, Button 2 freezes every time on the call to postTask.Result
. Why?
Targetting 4.0 or 4.5 makes no difference. The same code in a console application has no issues.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Net.Http.Handlers;
using System.Windows.Forms;
namespace WindowsFormsApplication13
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private ProgressMessageHandler _progressHandler = new ProgressMessageHandler();
private const string SERVICE_URL = "http://www.google.com.au";
private HttpClient GetClient(bool includeProgressHandler = false)
{
var handlers = new List<DelegatingHandler>();
if (includeProgressHandler)
{
handlers.Add(_progressHandler);
}
var client = HttpClientFactory.Create(handlers.ToArray());
client.BaseAddress = new Uri(SERVICE_URL);
return client;
}
private void PostUsingClient(HttpClient client)
{
var postTask = client.PostAsJsonAsync("test", new
{
Foo = "Bar"
});
var postResult = postTask.Result;
MessageBox.Show("OK");
}
private void button1_Click(object sender, EventArgs e)
{
using (var client = GetClient())
{
PostUsingClient(client);
}
}
private void button2_Click(object sender, EventArgs e)
{
using (var client = GetClient(true))
{
PostUsingClient(client);
}
}
}
}
Update
OK, so it looks like this is my issue. For .NET 4.5, the obvious solution would be, as @StephenCleary suggests, to let the async / await pattern permeate from the PostAsJsonAsync
call all the way down into the button-click handlers. Something more like this:
private Task<HttpResponseMessage> PostUsingClient(HttpClient client)
{
return client.PostAsJsonAsync("test", new
{
Foo = "Bar"
});
}
private async void button2_Click(object sender, EventArgs e)
{
var client = GetClient(true);
var response = await PostUsingClient(client);
}
Now my problem is getting an equivalent solution in .NET 4.0 (for legacy reasons, of course). A close approximation would be to use a continuation in the button-click handler, trouble is (again for legacy reasons), I actually want the button-click handler to block until the async returns. I've found some creative solutions using the 4.0 compatible yield
operator, but they feel a little messy. Instead, the simplest alternative I can devise is:
private void button2_Click(object sender, EventArgs e)
{
var result = Task.Run(() => { return PostUsingClient(client); }).Result;
}
I can't imagine this is the most performant implementation, and it still feels frankly clumsy. Can I do better?