C#异步方法 - 需要帮助

时间:2013-07-12 15:47:38

标签: c# asp.net-mvc asynchronous asp.net-web-api async-await

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

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

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

编辑1

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

存储库:

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 个答案:

答案 0 :(得分:5)

您是否从ASP.NET应用程序调用{​​{1}}?既然你已经将MVC标记为这个问题,我就是这么认为的。如果你这样做,那么你就是在陷入困境。

假设您像这样调用Web API。

GetAll().Result

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

像这样更改你的GetAll()。

Result()

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

最好将操作方法​​更改为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 } } 并等待public async Task<ActionResult> Index()

如果您觉得此答案有帮助,您只能感谢Stephen Cleary

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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

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