WebClient.DownloadDataTaskAsync 与 HttpClient.GetAsync

Joe*_*nos 5 c# asp.net asp.net-mvc async-await

我仍在了解 .NET 4.5 的各种异步功能,并且遇到了一些有趣的事情。鉴于我的 MVC 控制器中的以下内容,执行 (1) 和 (2) 时我得到不同的结果

public ActionResult Index() {
    var stuff = SomeExpensiveFunction();
    return View(stuff);
}
private byte[] SomeExpensiveFunction() {
    string url = "http://some-url.../";

    // (1)
    var wc = new WebClient();
    return wc.DownloadDataTaskAsync(url).Result;

    // (2)
    var hc = new HttpClient();
    return hc.GetAsync(url).Result.Content.ReadAsByteArrayAsync().Result;
}
Run Code Online (Sandbox Code Playgroud)

从表面上看,它们似乎是相同的 -WebClient.DownloadDataTaskAsyncHttpClient.GetAsync都是async返回Task. 版本WebClient返回,Task<byte[]>HttpClient版本返回Task<HttpResponseMessage>,我必须从中挖掘字节,但我正在.Result以任何一种方式调用,我希望在离开函数之前完成。

对于(1),我得到一个带有 的黄屏An asynchronous operation cannot be started at this time...。使用(2),一切正常。

我可以更改整个堆栈并使用async控制器方法本身和SomeExpensiveFunction,并且一切正常。但我试图找出 (1) 或WebClient使用 MVC 时的一般情况是否存在根本性错误。有什么想法吗?

编辑:我知道在这个示例中我可以使用这些调用的同步版本,因为我并没有真正异步执行任何操作 - 这只是一个基于更大代码库的示例。

Rob*_*don 2

您已经与 ASP.NET 的 SynchronizationContext 发生冲突。为了使 WebClient 示例正常工作,您应该使整个控制器异步。尝试这个:

public async Task<ActionResult> IndexAsync() {
    string url = "http://some-url.../";
    using (var wc = new WebClient())
        return View(await wc.DownloadDataTaskAsync(url));
}
Run Code Online (Sandbox Code Playgroud)

有关异步控制器的简介,请参阅http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4 ,以及http://www.tugberkugurlu.com/ archive/asynchronousnet-client-libraries-for-your-http-api-and-awareness-of-async-await-s-bad-effects用于解释 async/await 模式的死锁效果。