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 では要求コンテキストで一度に 1 つのスレッドしか許可されず、要求コンテキストで ( を使用してResult)スレッドをブロックするとasync、そのコンテキストに再入しようとしているメソッドが完了できないためです。

使用できるさまざまなハックがありますが、最もクリーンな解決策は、可能であれば同期コードを使用することです。別の解決策は を使用することResultですが、その場合、すべてawaitの in GetGeoAsync(およびasyncそれが呼び出すすべてのメソッド) が を使用していることを確認する必要があります。つまり、 、リクエスト、またはレスポンスをConfigureAwait(false)使用することはできません。HttpContext

かなりハックな 3 番目の解決策は、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 に答える