1

У меня есть этот метод асинхронной:Реализация синхронного метода с вызовом <asyncmethod> .Result

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); 
    } 
} 

Где FromJsonAsync реализован как метод расширения:

public async static Task<T> FromJsonAsync<T>(this string data, JsonSerializerSettings settings) where T : new() 
{ 
    return (T)(await JsonConvert.DeserializeObjectAsync<T>(data, settings)); 
} 

Теперь я хочу, чтобы добавить регулярные синхронные Post и я думал, что реализация будет такой:

public RES Post<RES>(string url, string content) where RES : new() 
{ 
    return PostAsync<RES>(url, content).Result; 
} 

Но это не действительно работа. Я вижу, что запрос отправляется через Hiffer sniffer, и я получаю ответ обратно, но я застреваю при отладке и не могу продолжить.

Кстати, это делает работу (с Result вместо 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); 
    } 
} 

Где FromJson реализован как метод расширения:

public static T FromJson<T>(this string data, JsonSerializerSettings settings) where T : new() 
{ 
    return (T)JsonConvert.DeserializeObject<T>(data, settings); 
} 

Приложение является веб-бэкенд (WebAPI).

Что я делаю неправильно?

+0

Какой вид приложения это? – i3arnon

+0

@ i3arnon - сервер веб-приложений (WebApi). –

+0

Мне показалось странным, что 'JsonConvert.DeserializeObjectAsync' - это тупики. Это просто оболочка 'Factory.StartNew' над' DeserializeObject', которая устарела BTW. –

ответ

2

У вас, вероятно, есть тупик на руках.

Asp.net использует SynchronizationContext для продолжения работы в контексте запроса. Если контекст заблокирован (как в вашем случае на PostAsync<RES>(url, content).Result), то продолжение не может быть выполнено, и поэтому метод async не может быть завершен, и у вас есть тупик.

Вы можете избежать этого с помощью 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); 
    } 
} 

Но лучше просто избежать блокировок синхронно на асинхронной коде для начала и имеющие две различных версий для синхронного и асинхронного.

+0

Хорошо. Это действительно сработало. Добавление ConfigureAwait (false) ко всем асинхронным вызовам предотвращает взаимоблокировки. Прочитав некоторые комментарии здесь, я думаю, что я буду дублировать свой код для синхронной операции, используя только синхронные вызовы. Спасибо за помощь. –

+1

@AmirPopovich Рассмотрите возможность использования только асинхронной версии без каких-либо синхронных перегрузок. Это не всегда возможно, но это направление, которое многие предпринимают. – i3arnon

0

Кажется, у вас есть функция non-async, и вы хотите запустить задачу, которая вызовет PostAsync, и дождитесь завершения этой задачи и возврата результата задачи. Это ваша проблема?

  • Чтобы начать Задачу, используйте Task.Run (() => ...);
  • Ожидание использования задачи Task.Wait (...);
  • Чтобы увидеть, если задача остановлена ​​из-за исключения: Task.IsFaulted
  • Результат задачи в Task.Result

Ваш код может быть:

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; 
} 
+0

Это не моя проблема. Вы просто завернули асинхронный вызов с помощью задачи. Спасибо за ответ в любом случае. –

2

Хотя возможно , Я бы не использовал ответ, предоставленный @ i3arnon. Как правило, вы не должны блокировать асинхронный код.Хотя ConfigureAwait(false) действительно работает, это может привести к путанице в вашей кодовой базе, где другие разработчики также могут блокировать с помощью .Result, не используя ConfigureAwait или понимая последствия этого.

Вместо подвергать синхронные методы, которые действительно синхронные:

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); 
    } 
} 
+0

Спасибо. Если я пойду синхронно, это, вероятно, будет использовать веб-клиент. –

+0

@Amir удачи;) –

Смежные вопросы