C#异步方法-需要帮助

本文关键字:帮助 异步方法 | 更新日期: 2023-09-27 18:22:04

我有一个异步方法,它使用Web API操作。它似乎陷入了一个循环。我的理由是,如果我把一个破发点放在接球块的第1线上,然后踩进去,它就永远不会真正击中第二条线。

最初,我返回了一个包含100000行以上(约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

为了回答一些问题,我有一个Web API项目,它被一个MVC项目引用。我不得不对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上得到了"一个任务被取消了。"异常。

堆栈跟踪。

C#异步方法-需要帮助

是否从ASP.NET应用程序调用GetAll().Result?既然你已经在这个问题上标记了MVC,我想是的。如果你这样做了,那么你就是在死锁。

假设你这样调用web API。

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

这里的问题是,当异步调用完成时,它需要在跳出来的原始线程上继续,但您通过调用Result()阻塞了该线程。对Result的调用阻塞并等待异步调用返回,异步继续等待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()

如果你觉得这个答案有帮助,你只需要感谢斯蒂芬·克利里。

您是否在外部库中工作?如果是,请尝试添加ConfigureAwait(false);

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

它与告诉等待有关,而不是捕捉当前的上下文。调用ConfigureAwait将确保使用ThreadPool执行方法的其余部分。关于这个主题的一些好的读物可以在斯蒂芬·克利里的博客上找到。

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

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