Reputation: 29836
I have this async method:
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
using (var client = new HttpClient())
{
HttpResponseMessage message = await client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json"));
var readAsStringAsync = await message.Content.ReadAsStringAsync();
return await readAsStringAsync.FromJsonAsync<RES>(mySerializerSettings);
}
}
Where FromJsonAsync
is implemented as an extension method:
public async static Task<T> FromJsonAsync<T>(this string data, JsonSerializerSettings settings) where T : new()
{
return (T)(await JsonConvert.DeserializeObjectAsync<T>(data, settings));
}
Now I want to add a regular synchronous Post
method and I thought the implementation would be:
public RES Post<RES>(string url, string content) where RES : new()
{
return PostAsync<RES>(url, content).Result;
}
But this doesn't really work. I see that the request is sent via a Http sniffer and I get a response back, but I get stuck when debugging and can't continue.
BTW, this does work (with Result
instead of await
):
public RES Post<RES>(string url, string content) where RES : new()
{
using (var client = new HttpClient())
{
HttpResponseMessage message = client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json")).Result;
var readAsStringAsync = message.Content.ReadAsStringAsync().Result;
return readAsStringAsync.FromJson<RES>(mySerializerSettings);
}
}
Where FromJson
is implemented as an extension method:
public static T FromJson<T>(this string data, JsonSerializerSettings settings) where T : new()
{
return (T)JsonConvert.DeserializeObject<T>(data, settings);
}
The application is a web backend (WebApi).
What am I doing wrong?
Upvotes: 1
Views: 1373
Reputation: 116538
You probably have a deadlock on your hands.
Asp.net uses a SynchronizationContext
to post continuations back to the request context. If the context is blocked (like it is in your case on PostAsync<RES>(url, content).Result
) then the continuation can't be executed and so the async method can't complete and you have a deadlock.
You can avoid it by using ConfigureAwait(false)
:
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
using (var client = new HttpClient())
{
HttpResponseMessage message = await client.PostAsync(url, new StringContent(content, Encoding.UTF8, "application/json"));
var readAsStringAsync = await message.Content.ReadAsStringAsync().ConfigureAwait(false);
return await readAsStringAsync.FromJsonAsync<RES>(mySerializerSettings).ConfigureAwait(false);
}
}
But it's better to just avoid blocking synchronously on async code to begin with and having two different versions for sync and async.
Upvotes: 2
Reputation: 149538
Although possible, I wouldn't use the answer provided by @i3arnon. Generally, you shouldn't block on async code. Although ConfigureAwait(false)
does work, it can lead to confusion in your code-base where other developers may also end up blocking using .Result
, without using ConfigureAwait
or understanding the implications of that.
Instead, expose synchronous methods which are really synchronous:
public RES Post<RES>(string url, string content) where RES : new()
{
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
var result = client.UploadString(url, content);
return JsonConvert.DeserializeObject<RES>(result, jsonSerializerSettings);
}
}
Upvotes: 2
Reputation: 30454
It seems you have a non-async function and you want to start a task that will call PostAsync and wait for this task to finish and return the result of the Task. Is this your problem?
Your code could be:
public async Task<RES> PostAsync<RES>(string url, string content) where RES : new()
{
// start the task that will call PostAsync:
var postTask = Task.Run( () => PostAsync(url, content));
// while this task is running you can do other things
// once you need the result: wait for the task to finish:
postTask.Wait();
// If needed check Task.IsFaulted / Task.IsCanceled etc. to check for errors
// the returned value is in Task.Result:
return postTask.Result;
}
Upvotes: 0