У меня есть приложение Blazor Server, которое вызывает несколько API. Все работает нормально, но я пытаюсь завернуть эти звонки в Tasks. Как только мой код доходит до вызова, все просто останавливается. Я уверен, что делаю что-то глупое, но никакие поиски в Google не находят мне решения. Вызов исходит из сетки Syncfusion при выборе строки. Вот мой минимальный воспроизводимый код:
public static IEnumerable<Quotation> customerQuotations = Array.Empty<Quotation>();
public async Task CustomerRowSelectHandler(RowSelectEventArgs<Customer> args)
{
GetCustomerQuotes(args.Data.customerId);
}
static async void GetCustomerQuotes(int customerId)
{
string url = string.Format(@"https://my.server.dns/quotations/customer/{0}", customerId);
var task = GetJsonString(url);
task.Wait();
customerQuotations = (IEnumerable<Quotation>)JsonConvert.DeserializeObject<Quotation>(task.Result);
}
private static async Task<string> GetJsonString(string url)
{
var TCS = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var myClient = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
HttpResponseMessage response = await myClient.GetAsync(url);
string streamResponse = await response.Content.ReadAsStringAsync();
TCS.SetResult(streamResponse);
return await TCS.Task;
}
Если я делаю это так же, как и все остальные вызовы, то есть не использую Task, все работает нормально, поэтому я знаю, что это не глупая ошибка, это то, чего мне не хватает в вызове Task.
Спасибо в ожидании любой предложенной помощи.
Основная проблема, это Task.Wait()
. Это может зайти в тупик.
public async Task CustomerRowSelectHandler(RowSelectEventArgs<Customer> args)
{
//GetCustomerQuotes(args.Data.customerId);
await GetCustomerQuotes(args.Data.customerId);
}
//static async void GetCustomerQuotes(int customerId)
async Task GetCustomerQuotes(int customerId)
{
string url = string.Format(@"https://my.server.dns/quotations/customer/{0}", customerId);
var task = GetJsonString(url);
// task.Wait();
await task;
customerQuotations = (IEnumerable<Quotation>)JsonConvert.DeserializeObject<Quotation>(task.Result);
}
и конечно
var task = GetJsonString(url);
await task;
... (task.Result)
может (должен) стать
string result = await GetJsonString(url);
... (result)
И когда вам не нужен объект ответа (для кода состояния и т. д.), все это можно сделать в 1 строке:
customerQuotations = await myClient.GetFromJsonAsync<Quotation[]>(url);
Похоже, вы слишком усложняете асинхронное кодирование в вызове API. Зачем вам нужно построить TaskCompletionSource
? У вас могут быть причины, но они не очевидны в коде вашего вопроса.
Почему бы не что-то вроде этого:
public async Task CustomerRowSelectHandler(...)
{
await GetCustomerQuotes(...);
}
private async ValueTask GetCustomerQuotes(...)
{
string url = string.Format(@"....");
var http = new HttpClient(...);
HttpResponseMessage response = await http.GetAsync(url);
if (response.IsSuccessStatusCode)
customerQuotations = await response.Content.ReadFromJsonAsync<IEnumerable<Quotation>>() ?? Enumerable.Empty<Quotation>(); ;
// handle errors
}
Или даже это, но вы теряете перехват ошибок.
customerQuotations = await http.GetFromJsonAsync<IEnumerable<Quotation>>(url);
Вам также следует рассмотреть возможность использования IHttpClientFactory
для управления экземплярами http.
MrC - большое спасибо, что нашли время, чтобы помочь мне. Я не разработчик, просто парень, пытающийся выжить, поэтому, когда люди с большим опытом берутся за эту проблему, это действительно много значит. Еще раз спасибо.
Просто для полноты, TaskCompletionSource взят из другой статьи, за которой я следил. Этого больше нет, еще одна причина поблагодарить MrC! Пытаюсь реализовать IHttpClientFactory и получаю ошибку, которую я разместил здесь: stackoverflow.com/questions/75334007/…
TaskCompletionSource предназначен для более специализированных и сложных ситуаций. Здесь совершенно ненужно. Видишь, ты разобрался со своей проблемой.
Каждый день - учебный день! Еще раз спасибо за вашу помощь.
Всегда избегайте
async void