C#异步方法 – 需要帮助

我有一个消耗Web API操作的异步方法。 它似乎陷入了一个循环。 我的理由是因为如果我在catch块的第1行放置一个断点,并且进入,它实际上从未击中第二行。

我最初返回一个包含100,000+(~30mb)行的数据集,并认为由于请求的大小,它可能会很慢,但在更改我的Web API操作以仅返回1行后,问题仍然存在。 当我浏览到Web API解决方案的URI时,肯定会返回数据,我在浏览器中返回了JSON。

public async Task<IEnumerable> GetAll() { try { var response = await _client.GetAsync( string.Format("{0}/api/document", _baseURI)); // never hits line below return await response.Content.ReadAsAsync() as IEnumerable; } catch (Exception ex) { // handle exception } } 

我不确定我在这里遗失了什么? 一些帮助将不胜感激。

编辑1

在回答一些问题时,我有一个由MVC项目引用的Web API项目。 我必须对JSON反序列化的原始问题进行一些更改。

库:

 public async Task<IEnumerable> GetAll() { try { string json; var response = await _client.GetAsync(string.Format( "{0}/api/document", _baseURI)).ConfigureAwait(false); var resource = await response.Content.ReadAsAsync(); using(var reader = new StreamReader(resource.ToString())) { json = reader.ReadToEndAsync().ToString(); } return JsonConvert.DeserializeObjectAsync(json) as IEnumerable; } catch (Exception) { throw; } } 

控制器:

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

对于以下答案中描述的更改,如上所述进行调试时仍会出现同样的问题。 但是我得到“任务被取消了”。 存储库中方法中的catch的exception。

堆栈跟踪。

你在调用GetAll().Result来自ASP.NET应用程序的GetAll().Result ? 既然你已经将MVC标记为这个问题,我就是这么认为的。 如果你这样做,那么你就是在陷入僵局。

假设您像这样调用Web API。

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

这里的问题是当异步调用完成时,它需要在跳出的原始线程上继续,但是你通过调用Result()来阻塞该线程。 调用Result块并等待异步调用返回,async continuation等待ASP.NET线程继续可用。 那是一个死锁。

像这样更改你的GetAll()。

 public async Task> GetAll() { try { var response = await _client.GetAsync( string.Format("{0}/api/document", _baseURI)) .ConfigureAwait(false); // never hits line below return await response.Content.ReadAsAsync() as IEnumerable; } catch (Exception ex) { // handle exception } } 

这表明当异步调用完成时不需要保留上下文,因此继续发生在线程池线程(不是ASP.NET)中。

更好的是将action方法更改为public async Task Index()并等待GetAll()

如果您觉得这个答案有帮助,那么您一定要感谢Stephen Cleary 。

你在外部图书馆工作吗? 如果是这样,尝试添加ConfigureAwait(false);

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

它与告诉等待不捕获当前上下文有关。 调用ConfigureAwait将确保使用ThreadPool执行该方法的其余部分。 关于这个主题的一些好的阅读可以在Stephen Cleary的博客上找到。

如果它适用于Web浏览器但不使用代码,那么您可能需要发送一个accept标头。 添加以下行

 _client.DefaultRequestHeaders.Accept = new MediaTypeWithQualityHeaderValue("application/json");