Not getting past GetAwaiter().GetResult() - c#

I have a PostAsync method in an internal part of my code that doesn't seem to ever return a response. However, I use it synchronously via .GetAwaiter().GetResult(). The target framework is net45.
public async Task<TResponse> PostAsync<TResponse, TRequest>(string method, TRequest body)
{
_logger.Log($"Method {method}, body {JsonConvert.SerializeObject(body)} on url {_configuration.ApiUrl}");
using (var customDelegatingHandler = new HMACDelegatingHandler(_configuration, _apiId))
{
using (var client = new HttpClient(customDelegatingHandler))
{
var response = await client.PostAsync($"{_configuration.ApiUrl}/{method}",
new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"));
if (response.StatusCode == HttpStatusCode.OK)
{
var content = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TResponse>(content);
}
else
{
await Log(body, response);
}
return default;
}
}
}
What I do is call the PostAsync in another method:
public async Task<decimal> GetBalance(Request request)
{
// = new MyCustomClient...
QueryFundsResponse response = await customClient.PostAsync<Response, Request>("testAction", request);
if (response == default)
return 0.0m;
return response.Amount;
}
Then, finally at the very top of the flow, I call the GetBalance method like this:
var sw = new Stopwatch();
sw.Start();
var balance = _provider
.GetBalance(request)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
sw.Stop();
_logger.Log($"GetBalance -> method duration: { sw.ElapsedMilliseconds }");
I don't see the log in my logs at all, and I don't seem to ever get a response or any code executed after the .GetAwaiter().GetResult(). Switching the last block of code to be asynchronous and await the GetBalance() method is not really an option for me, sadly.
I am unable to figure out why nothing is changing, even after using the .ConfigureAwait(false) method.

You're experiencing the common deadlock that happens when you block on asynchronous code (described in detail on my blog). There are a variety of ways to get around it, but they're all hacks and none of them work in every situation.
In your case, I'd say either use the direct blocking hack or use the boolean argument hack.
The direct blocking hack requires you to use ConfigureAwait(false) everywhere. Note that your current code only uses ConfigureAwait(false) where it doesn't do anything; ConfigureAwait configures the await, so it needs to go where the awaits are. All of them.
The boolean argument hack means that your code will take a bool parameter that determines whether it executes synchronously or asynchronously. Note that HttpClient (for now) has an async-only API, so your custom delegating handler will need to support direct blocking, using ConfigureAwait(false). Similarly, Log will either need a synchronous equivalent or also support direct blocking. Your code would end up looking something like this:
public Task<TResponse> PostAsync<TResponse, TRequest>(string method, TRequest body) => PostCoreAsync(method, body, sync: false);
public TResponse Post<TResponse, TRequest>(string method, TRequest body) => PostCoreAsync(method, body, sync: true).GetAwaiter().GetResult();
private async Task<TResponse> PostCoreAsync<TResponse, TRequest>(string method, TRequest body, bool sync)
{
_logger.Log($"Method {method}, body {JsonConvert.SerializeObject(body)} on url {_configuration.ApiUrl}");
using (var customDelegatingHandler = new HMACDelegatingHandler(_configuration, _apiId))
{
using (var client = new HttpClient(customDelegatingHandler))
{
var responseTask = client.PostAsync($"{_configuration.ApiUrl}/{method}",
new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"));
var response = sync ? responseTask.GetAwaiter().GetResult() : await responseTask;
if (response.StatusCode == HttpStatusCode.OK)
{
var content = sync ? response.Content.ReadAsStringAsync().GetAwaiter().GetResult() : await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<TResponse>(content);
}
else
{
var logTask = Log(body, response);
if (sync)
logTask.GetAwaiter().GetResult();
else
await logTask;
}
return default;
}
}
}
public Task<decimal> GetBalanceAsync(Request request) => GetBalanceCoreAsync(request, sync: false);
public decimal GetBalance(Request request) => GetBalanceCoreAsync(request, sync: true).GetAwaiter().GetResult();
private async Task<decimal> GetBalanceCoreAsync(Request request, bool sync)
{
// = new MyCustomClient...
QueryFundsResponse response = sync ?
customClient.Post<Response, Request>("testAction", request) :
await customClient.PostAsync<Response, Request>("testAction", request);
if (response == default)
return 0.0m;
return response.Amount;
}
var sw = new Stopwatch();
sw.Start();
var balance = _provider
.GetBalance(request);
sw.Stop();
_logger.Log($"GetBalance -> method duration: { sw.ElapsedMilliseconds }");

Related

Async Interception using Castle dynamic proxy

I'm trying to build a dynamic http client using dynamic proxy to intercept the calls and create an http request with it.
The issue i had was with Async methods:
private Task<object> PostAsync(HttpClient client, string url, HttpRequestParameters parameters, Type returnType)
{
return Task.Run(async () =>
{
var requestContent = new StringContent(Serializer.Serialize(parameters.BodyParameters));
var httpResponse = await client.PostAsync(url, requestContent);
var responseContent = await httpResponse.Content.ReadAsStringAsync();
return Serializer.Deserialize(responseContent, returnType);
});
}
My task returns dynamic/object and not the T of the Interception return type.
I thought that i will be able to use it like so
var task = PostAsync(client, url, parameters, returnType);
invocation.ReturnValue = task;
Since the task that will be returned is the original task and it is still pending i thought it would work but all i'm getting is an exception that Task cant be converted to task of my type (Which is string in that case).
Thanks for the helpers
Edit:
I did see Intercept async method, that's what i tried to do but i was unable to call the Task even using reflection, i still got the same exception.
I solved it eventually with a few modifications:
Creating the interceptor with a base object, i used Moq objects to lazy create them and store them in a ConcurrentDictionary for caching.
var mock = new Mock<T>();
var pg = new ProxyGenerator();
return pg.CreateInterfaceProxyWithTarget<T>(GetTarget(clientType), _gatewayInterceptor);
I passed the invocation's return value (in that case Task of T) to a method and got the T.
I wrapped the http call with a new Task of T, await the http call and
return desirialized T result from the task.
Assign the new Task of T back to the return value.
invocation.ReturnValue = GetAsync((dynamic)invocation.ReturnValue, serializer, headers, req);
internal static Task<T> GetAsync<T>(Task<T> originalTask, ISerializer serializer, Headers headers, InvokeHttpRequest req)
{
return Task.Run(async () =>
{
using (HttpClient client = new HttpClient())
{
var httpResponse = await PerformGetAsync(headers, req, client);
var jsonResponse = await httpResponse.Content.ReadAsStringAsync();
return ProcessResult<T>(serializer, jsonResponse);
}
});
}
I know its not the best way to go but it worked for me.
The solution is here if anyone needs it https://github.com/ErezLevip/SimpleProxyClient

Difference between these two async functions

Hello I dont get the difference between the following two asnyc functions, could someone explain it to me? Both of them doesnt return IRestResponse, so I cant access StatusCode field. Do I have to cast here?
public async Task<IRestResponse> Post<T>(string Ressource, T ObjTOPost) where T : new()
{
return await Task.Factory.StartNew(() =>
{
var client = new RestClient("test.com");
var request = new RestRequest(Ressource, Method.POST);
var response = client.Execute(request);
return response;
});
}
And this:
public async Task<IRestResponse> Post<T>(string Ressource, T ObjTOPost) where T : new()
{
var client = new RestClient("test.com");
var request = new RestRequest(Ressource, Method.POST);
var response = await client.ExecuteTaskAsync<T>(request);
return response;
}
Both of them doesnt return IRestResponse, so I cant access StatusCode field.
They return a Task<IRestResponse>. You can get the interface by awaiting the task, e.g.
var task = Post(resource, objectToPost);
IRestResponse response = await task;
Or in one line (more common):
var response = await Post(resource, objectToPost);
Difference between these two async functions
The second example is far more straightforward. The first example spins up an additional task and passes its awaitable back to the caller, whereas the second example awaits the RestClient directly. I see no reason to use the structure in the first example.

C# post request stuck in WaitingForActivation even with timeout

I am trying to do a simple post request using HttpClient (which as far as I can tell is the current correct way to handle post requests.) However, the code is stalling on timeoutTask.Result, with the tasks stuck on Status=WaitingForActivation. Looking at the server side of things, I can tell that the post request is being sent and data is being returned, and I have also called wget manually and verified that some text data is correctly returned from the server for this post request.
This happened when I simply called task.Result, so I tried to add a timeout task, but I must be making some mistake with async because it still hangs on timeoutTask.Result.
class HttpPostExample
{
private static readonly HttpClient client = new HttpClient();
public void processSentence()
{
string uri = "http://localhost:9000/?properties={%22annotators%22%3A%22tokenize%2Cssplit%2Cpos%22%2C%22outputFormat%22%3A%22json%22}";
string result = postSync(uri, "the quick brown fox jumped over the lazy dog");
Debug.WriteLine(result);
}
public static async Task<string> postAsync(string uri, string postData)
{
var content = new StringContent(postData);
var response = await client.PostAsync(uri, content);
var responseContent = await response.Content.ReadAsStringAsync();
return responseContent;
}
public static async Task<string> runTaskWithTimeout(Task<string> task, double seconds)
{
if (await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(seconds))) == task)
{
// task completed within timeout
return task.Result;
}
else
{
// timeout logic
return "timed out";
}
}
public static string postSync(string uri, string postData)
{
var task = postAsync(uri, postData);
var timeoutTask = runTaskWithTimeout(task, 2.0);
return timeoutTask.Result;
}
}
Since you are calling an async method from a synchronous method you may want to try and add ConfigureAwait(false) on all of your await lines. Generally it is good idea to use async all the way or add ConfigureAwait(false) to avoid deadlocks.
Check out this blog post for an example: https://medium.com/bynder-tech/c-why-you-should-use-configureawait-false-in-your-library-code-d7837dce3d7f

.NET HttpClient hangs when using await keyword?

After considering this interesting answer HttpClient.GetAsync(...) never returns..., I still have a situation where my HttpClient is not returning when I use await (sample code below). In addition. I use this helper routine from both asp.net MVC5 Controllers (UI-driven) and the WebApi. What can I do to:
Use await instead of the (dreaded) .Result and still have this function return?
Reuse this same routine from both MVC Controllers and WebApi?
Apparently, I should replace the .Result with .ConfigureAwait(false) but this seems to contradict with the fact that "Task4" in the post cited above works fine with an await httpClient.GetAsync. Or do I need separate routines for Controller and WebApi cases?
public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
if (string.IsNullOrEmpty(elasticQuery)) return null;
IEnumerable<TcMarketUserFullV1> res = null;
using (var hclient = new HttpClient())
{
hclient.BaseAddress = new Uri("https://addr.servicex.com");
hclient.DefaultRequestHeaders.Accept.Clear();
hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
CloudConfigurationManager.GetSetting("jwt-bearer-token"));
// Why does this never return when await is used?
HttpResponseMessage response = hclient.GetAsync("api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result;
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
}
else{log.Warn("...");}
}
return res;
}
UPDATE: My call chain, which starts with a Telerik Kendo Mvc.Grid DataBinding call is as follows:
[HttpPost]
public async Task<ActionResult> TopLicenseGrid_Read([DataSourceRequest]DataSourceRequest request)
{
var res = await GetLicenseInfo();
return Json(res.ToDataSourceResult(request)); // Kendo.Mvc.Extensions.DataSourceRequest
}
Then:
private async Task<IEnumerable<CsoPortalLicenseInfoModel>> GetLicenseInfo()
{
...
// Never returns
var qry = #"app_metadata.tc_app_user.country:""DE""";
return await TcSearchMultiUsersAsync(qry);
}
Then, the routine shown in full above but now WITHOUT the .Result:
public static async Task<IEnumerable<TcMarketUserFullV1>> TcSearchMultiUsersAsync(string elasticQuery)
{
if (string.IsNullOrEmpty(elasticQuery)) return null;
IEnumerable<TcMarketUserFullV1> res = null;
using (var hclient = new HttpClient())
{
hclient.BaseAddress = new Uri("https://addr.servicex.com");
hclient.DefaultRequestHeaders.Accept.Clear();
hclient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
hclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",
CloudConfigurationManager.GetSetting("jwt-bearer-token"));
// This now returns fine
HttpResponseMessage response = hclient.GetAsync("api/v2/users?search_engine=v2&q=" + elasticQuery");
if (response.IsSuccessStatusCode)
{
// This returns my results fine too
var content = await response.Content.ReadAsStringAsync();
// The following line never returns results. When debugging, everything flows perfectly until I reach the following line, which never
// returns and the debugger returns me immediately to the top level HttpPost with a result of null.
res = JsonConvert.DeserializeObject<TcMarketUserFullV1[]>(content).AsEnumerable();
}
else{log.Warn("...");}
}
return res;
}
You need to await everything. It's not enough that you await on one of them, you need to await on all of them:
This:
HttpResponseMessage response = hclient.GetAsync(
"api/v2/users?q=" + elasticQuery + "&search_engine=v2").Result;
Should be:
HttpResponseMessage response = await hclient.GetAsync(
"api/v2/users?q=" + elasticQuery + "&search_engine=v2");
It is enough to have one blocking call to .Result in order to deadlock. You need "async all the way".

Implementing a synchronous method with calling the <asyncmethod>.Result

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?
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.
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);
}
}
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?
To start a Task, use Task.Run( () => ...);
To wait for the Task use Task.Wait(...);
To see if the task stopped because of an exception: Task.IsFaulted
The result of the task is in Task.Result
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;
}

Categories

Resources