0

I've the following function but can't debug it because response never come. Service on the other side is working. Any help will be preciated, I can't deduce how must be do it with other answers in SO

    public async Task<string> PostObjectToWebServiceAsJSON(object objectToPost, string validatorName, string method)
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("myuri" + "/" + method);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            // HTTP POST
            var response = await client.PostAsJsonAsync("", objectToPost);
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                string errplugin = "Error";

                return null;
            }
        }
    }

This is how I call it:

    public PaymentResponseInternal Post(some stuff here)
    {
        Task<string> retornoPluginAsync = PostObjectToWebServiceAsJSON(some stuff here);

        retornoPluginAsync.Wait();
        string result = retornoPluginAsync.Result;

        return JsonConvert.DeserializeObject<PaymentResponseInternal>(result);
    }
Leandro Bardelli
  • 10,561
  • 15
  • 79
  • 116
  • When you say "response never come", what do you mean? Is it hanging? How are you calling this function? It's likely a deadlock. – DavidG Jan 08 '18 at 14:10
  • Yes, it goes to the service, the service response but doesn't do anymore after. Let me update the question with how I call it – Leandro Bardelli Jan 08 '18 at 14:11
  • 7
    So yeah, it's a deadlock because you are calling `retornoPluginAsync.Result`. You need to `await` that call. See here: https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – DavidG Jan 08 '18 at 14:13
  • 1
    It sounds like `public PaymentResponseInternal Post(some stuff here)` should be `public async Task Post(some stuff here)` and should internally make use of `await`. – David Jan 08 '18 at 14:14
  • I miss an "await" in my question, sorry. I update it! but let me see what are you writing here. Thanks! – Leandro Bardelli Jan 08 '18 at 14:15
  • Sorry but how I transform Task to PaymentResponseInternal then? – Leandro Bardelli Jan 08 '18 at 14:17
  • 2
    You await that function too. async code is like a virus, once you start, it will slowly consume your whole project (which is a good thing) – DavidG Jan 08 '18 at 14:18
  • lol!!! but I can't do that because I come from other call that can't wait :( – Leandro Bardelli Jan 08 '18 at 14:20
  • 1
    Then look at [this](https://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c), unfortunately, only you will be able to tell which is the most suitable method to solve your issue. – DavidG Jan 08 '18 at 14:21
  • @Sven that is the weird scenario that motivates this question, i get the data posted in the other service, and the service sends a response, but nothing happens :S – Leandro Bardelli Jan 08 '18 at 14:35

2 Answers2

3

As mentioned in the comments, you should make PaymentResponseInternal an async method that returns a Task<PaymentResponseInternal> and await it.

You shouldn’t mix synchronous and asynchronous code. One of the reasons for this is that you might deadlock as you have discovered here. Please refer to @Stephen Cleary's article on the subject for more information: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

If you can't make your Post method async for some reason, you could try not to capture the context in the PostObjectToWebServiceAsJSON method by calling ConfigureAwait(false) after each await:

public async Task<string> PostObjectToWebServiceAsJSON(object objectToPost, string validatorName, string method)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri("myuri" + "/" + method);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        var response = await client.PostAsJsonAsync("", objectToPost).ConfigureAwait(false);
        if (response.IsSuccessStatusCode)
        {
            return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        }
        else
        {
            return null;
        }
    }
}

Please refer to @Stephen Cleary's blog post for information about this: https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

mm8
  • 163,881
  • 10
  • 57
  • 88
1

With your update to the question, you're running into a deadlock situation when you call PostObjectToWebServiceAsJSON synchronously. You should add a call to retornoPluginAsync.ConfigureAwait(false); before you Wait() for it. ConfigureAwait(false) configures the Task to not require the original call context and should, in your case, solve the deadlock.

    public PaymentResponseInternal Post(some stuff here)
    {
        Task<string> retornoPluginAsync = PostObjectToWebServiceAsJSON(some stuff here);
        retornoPluginAsync.ConfigureAwait(false); // Add this
        retornoPluginAsync.Wait();
        string result = retornoPluginAsync.Result;

        return JsonConvert.DeserializeObject<PaymentResponseInternal>(result);
    }
GeorgDangl
  • 2,146
  • 1
  • 29
  • 37