2
public class GeoHelper
{ 
    const string GeoIpUrl = "http://freegeoip.net/json/{0}";
    private readonly string _ipAddress = string.Empty;

    public GeoHelper(string ip)
    {
        _ipAddress = ip;
    }

    public async Task<string> GetGeoAsync()
    {
        string uri = string.Format(GeoIpUrl, _ipAddress);

        var httpClient = new HttpClient();

        var response = await httpClient.GetAsync(uri);

        response.EnsureSuccessStatusCode();

        var content = await response.Content.ReadAsStringAsync();

        return content;
    }

}

然后我这样称呼它:

    [ChildActionOnly]
    public ActionResult UserGeo()
    {
        var ip = RequestHelper.GetClientIpAddress(Request);

        var geoHelper = new GeoHelper(ip);

        var response = geoHelper.GetGeoAsync();

        var result = response.Result;

        var resultobj = JsonConvert.DeserializeObject<GeoInfo>(result);

        return Content(resultobj.city);
    }

var result = response.Result;它等待并且永无止境的过程中,我在等待时得到了白发。我有一个小型控制台应用程序,它在那里运行良好。相同的代码。

为什么?我该如何解决?

4

2 回答 2

5

不幸的是,MVC 目前不支持异步子操作。请对此问题投票(CodePlexUserVoice)。

我在我的博客上解释了死锁问题。本质上,这是因为 ASP.NET 在请求上下文中一次只允许一个线程,并且当您在请求上下文中阻塞一个线程时(使用Result),任何async尝试重新进入该上下文的方法都无法完成。

您可以使用各种技巧,但最干净的解决方案是尽可能使用同步代码。另一种解决方案是使用Result,但在这种情况下,您必须确保每个awaitin GetGeoAsync(以及async它调用的每个方法)都使用ConfigureAwait(false),这意味着您不能使用HttpContext、请求或响应。

第三种解决方案相当老套,是AsyncContext.Run从我的AsyncEx 库中使用:

[ChildActionOnly]
public ActionResult UserGeo()
{
  return AsyncContext.Run(async () =>
  {
    var ip = RequestHelper.GetClientIpAddress(Request);
    var geoHelper = new GeoHelper(ip);
    var response = geoHelper.GetGeoAsync();
    var result = await response;
    var resultobj = JsonConvert.DeserializeObject<GeoInfo>(result);
    return Content(resultobj.city);
  }
}

但是,该AsyncContext方法不适用于所有代码。它设置了一种“嵌套循环”但不使用AspNetSynchronizationContext,因此一些 ASP.NET 代码不喜欢在AsyncContext.

于 2013-09-07T11:53:33.203 回答
0

尝试这个...

[ChildActionOnly]
public async Task<ActionResult> UserGeo()
{
    var ip = RequestHelper.GetClientIpAddress(Request);
    var geoHelper = new GeoHelper(ip);
    var result = await geoHelper.GetGeoAsync();
    var resultobj = JsonConvert.DeserializeObject<GeoInfo>(result);
    return Content(resultobj.city);
}
于 2013-09-07T11:07:10.557 回答