C # HTTP-запрос работает в консольном приложении, но не в WinForms - PullRequest
1 голос
/ 20 мая 2019

Первоначально я создал консольное приложение, чтобы провести некоторое тестирование с получением вызовов API с сервера, и получил его полностью функциональным и работающим.Теперь я хочу сделать отображение, но тот же самый точный код из консольного приложения не будет работать в приложении winforms.Из-за некоторого тестирования точки останова я видел, что он зависает в этой строке:

var response = await httpClient.SendAsync(request);

Я не понимаю, в чем разница, я скачал все необходимые пакеты.Я полагаю, это связано с использованием await.Спасибо, Итан

Вот код метода запроса, за исключением личной информации.Я понимаю, что в конце есть некоторые ненужные компоненты, но я просто копировал и вставлял из моего первоначального консольного приложения.Все методы, которые используют этот метод, включая main, являются асинхронными.

public async Task<string> APICall(string address)
    {

        string error = "An error was encountered.";
        using (var httpClient = new HttpClient())
        {
            using (var request = new HttpRequestMessage(new HttpMethod("GET"), address))
            {
                request.Headers.TryAddWithoutValidation("Api-Key", "API KEY");
                httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/hal+json"));

                var response = await httpClient.SendAsync(request);
                if (response.IsSuccessStatusCode)
                {
                    try
                    {
                        Stream res = await response.Content.ReadAsStreamAsync();
                        var serializer = new JsonSerializer();

                        using (var sr = new StreamReader(res))
                        {
                            using (var jsonTextReader = new JsonTextReader(sr))
                            {
                                return await response.Content.ReadAsStringAsync();


                            }

                        }


                    }
                    catch (IOException e)
                    {
                        Console.WriteLine(e);
                    }
                    catch (NullReferenceException e)
                    {
                        Console.WriteLine(e);
                    }
                }

                request.Dispose();
            }
            httpClient.Dispose();
        }
        return error;
    }

Это вызывающие методы, где вызывается проблемный метод, он переходит от Main к startAsync к CallAsync, к APICall, к рассматриваемому методу.

        static async Task Main(string[] args)
    {

                    Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Form1 f = new Form1();
        Caller caller = new Caller();
        string test = await caller.CallAsync();
        foreach (KeyValuePair<string, Server> server in Server.servers)
        {
            f.AddRow(server.Value.name, server.Value.location, server.Value.status, server.Value.ticketStatus);
       }
        Application.Run(f);


    }

public static async Task startAsync()
    {
        Caller caller = new Caller();
        string json = await caller.CallAsync();
        Console.WriteLine(json);
        dynamic name = JsonConvert.DeserializeObject<RootObject>(json);
        List<Location> locs = name._embedded.locations;
        int count = 0;
        foreach (Location loc in locs)
        {
            count++;
            Console.WriteLine(count);
            Server.servers.Add(loc.name, new Server(loc.name, loc.address.city, loc.address.state, loc.id, loc.status, await caller.TicketStatusAsync(loc.id)));

        }
    }
        public async Task<string> CallAsync()
    {
        return await APICall(baseAddress);
    }

1 Ответ

3 голосов
/ 20 мая 2019

Скорее всего, дальше по стеку вызовов вы звоните Wait() или Result или GetAwaiter().GetResult().Другими словами, ваш код синхронизирован по асинхронному.Это прекрасно работает в консольном приложении - и на самом деле, некоторая блокировка, подобная этой, необходима , поэтому основной поток консоли не завершается до завершения асинхронной работы - но он блокируется в других контекстах, включая WinForms.

Причина, по которой он блокируется, заключается в том, что await по умолчанию захватывает контекст и использует этот контекст для возобновления выполнения метода async.Поэтому, когда ваш код await httpClient... выполняется, он захватывает текущий контекст, а затем возвращает незавершенную задачу.Ваш вызывающий код дальше по стеку будет затем блокироваться, ожидая завершения этой задачи.

Для потока пользовательского интерфейса WinForms этот контекст является WinFormsSynchronizationContext, который всегда выполняет код в потоке пользовательского интерфейса.Таким образом, когда SendAsync завершится, он возобновит выполнение метода async в потоке пользовательского интерфейса.Однако этот поток пользовательского интерфейса заблокирован, ожидая завершения задачи.Задача не может быть завершена до тех пор, пока поток пользовательского интерфейса не освободится, поэтому у вас есть тупик.

Правильный способ устранения этого тупика состоит в том, чтобы удалить антипаттерн синхронизации-over-async .Другими словами, замените Wait() или Result или GetAwaiter().GetResult() на await.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...