2013-07-12 5 views
0

У меня есть асинхронный метод, который использует действие веб-API. Кажется, он застрял в петле. Мое рассуждение для этого состоит в том, что, если я поставлю точку останова в строке 1 блока catch и перейду на нее, она никогда не попадет во вторую строку.C# асинхронный метод - нужна помощь

Первоначально я возвращал набор данных со 100 000+ (~ 30 МБ) строк и думал, что он может быть просто медленным из-за размера запроса, но после изменения моего действия веб-API для возврата только 1 строки проблема все еще сохранялось. Данные, безусловно, возвращаются, когда я просматриваю URI решения Web API, которое я получаю JSON в моем браузере.

public async Task<IEnumerable<Document>> GetAll() 
{ 
    try 
    { 
     var response = await _client.GetAsync(
         string.Format("{0}/api/document", _baseURI)); 

     // never hits line below 
     return await response.Content.ReadAsAsync<Document>() 
        as IEnumerable<Document>; 
    } 
    catch (Exception ex) 
    { 
     // handle exception 
    } 
} 

Я не уверен, что здесь что-то отсутствует? Некоторая помощь будет оценена по достоинству.

EDIT 1

В ответ на некоторые вопросы, у меня есть API проекта Web, который ссылается проект MVC. Мне пришлось внести некоторые изменения из оригинального вопроса для десериализации JSON.

Repository:

public async Task<IEnumerable<Document>> GetAll() 
{ 
    try 
    { 
     string json; 
     var response = await _client.GetAsync(string.Format(
         "{0}/api/document", _baseURI)).ConfigureAwait(false); 

     var resource = await response.Content.ReadAsAsync<Document>(); 

     using(var reader = new StreamReader(resource.ToString())) 
     { 
      json = reader.ReadToEndAsync().ToString(); 
     } 

     return JsonConvert.DeserializeObjectAsync<Document>(json) 
         as IEnumerable<Document>; 
     } 
     catch (Exception) 
     { 
      throw; 
     } 
    } 

Контроллер:

public async Task<ActionResult> GetAll() 
{ 
    return PartialView("_GetAllDocumentsPartial", await _docRepo.GetAll()); 
} 

С изменениями, описанными в ответах ниже, та же самая проблема все еще происходит при отладке, как указано выше. Однако я получаю «Задача была отменена». исключение в catch в методе в репозитории.

Stack trace.

+3

** Никогда ** не пишите 'throw ex'. http://stackoverflow.com/a/2999314/34397 – SLaks

+1

Вы уверены, что он никогда не попадает во вторую строчку? есть проблемы с асинхронизацией и отладкой ... вы можете попробовать поставить точку останова на второй строке, а также просто убедиться. – Tim

+0

Да, только что поставил точку останова, и он не ударил его. Chrome просто продолжает загружаться/страница пуста. – MattSull

ответ

5

Вы звоните GetAll().Result из приложения ASP.NET? Так как вы отметили MVC на этот вопрос, я так и предполагаю. Если вы это сделаете, вы забудете себя.

Скажите, что вы называете веб-API таким.

public ActionResult Index() 
{ 
    var result = GetAll().Result; 

    return View(); 
} 

Проблемы здесь, когда завершается вызов асинхронного, он должен продолжать оригинальную нить, где она выскочила, но вы блокируете этот поток путем вызова Result(). Позвоните в блок результатов и ожидайте возвращения асинхронного вызова, и продолжение async ждет, когда поток ASP.NET будет доступен для продолжения. Это мертвый замок.

Измените свой GetAll() следующим образом.

public async Task<IEnumerable<Document>> GetAll() 
{ 
    try 
    { 
     var response = await _client.GetAsync(
         string.Format("{0}/api/document", _baseURI)) 
          .ConfigureAwait(false); 

     // never hits line below 
     return await response.Content.ReadAsAsync<Document>() 
        as IEnumerable<Document>; 
    } 
    catch (Exception ex) 
    { 
     // handle exception 
    } 
} 

Это указывает на контекст не должен быть сохранен, когда вызов асинхронной завершена, и, следовательно, продолжение происходит в потоке пула потоков (не ASP.NET).

Еще не измените метод действия на public async Task<ActionResult> Index() и ждите GetAll().

Если вы найдете этот ответ полезным, вы должны поблагодарить Stephen Cleary.

+0

Извинения в задержке в ответе. Я никогда не делал так, как вы описали в своем ответе, т. Е. 'Var result = GetAll(). Результат;'. См. Обновленный вопрос. – MattSull

+0

Когда я добавляю '.ConfigureAwait (false)' и отлаживаю его шаги на следующую строку, но с этим на месте respsonse не содержит определения для 'content', то есть я не могу пойти' respsonse.content ... '. Как я могу получить доступ к «Результату» ответа, когда я использую '.ConfigureAwait (false)'? – MattSull

+0

Что-то вроде 'var x = await response.GetAwaiter(). GetResult() ...' работает. Некоторые другие незначительные проблемы, мешающие мне добраться до того места, где я хочу быть, возможно, для другого вопроса. – MattSull

1

Ты работаешь во внешнюю библиотеку? Если да, попробуйте добавить ConfigureAwait (false);

var response = await _client.GetAsync(string.Format("{0}/api/document", _baseURI)) 
          .ConfigureAwait(false); 

Его обязанность говорить, чтобы не захватывать текущий контекст.Вызов ConfigureAwait гарантирует, что остальная часть метода будет выполнена с использованием ThreadPool. Некоторые хорошие чтения по теме можно найти here в блоге Стивена Клири.

+0

Да У меня есть проект MVC, который использует проект веб-API. Я внес изменения в соответствии с вашим ответом. См. Обновленный вопрос. – MattSull

0

Если он работает с веб-браузером, но не использует код, вам, вероятно, необходимо отправить заголовок принятия. Добавить следующую строку

_client.DefaultRequestHeaders.Accept = new MediaTypeWithQualityHeaderValue("application/json"); 
Смежные вопросы