2013-06-07 18 views
5

私はasync/awaitに1週間苦労しています。いくつかの背景:以下のコードは、MVC4ウェブサイトプロジェクトの一部です。ウェブサイトには多数のAPI呼び出しが発生しています。目標は、これらのAPI呼び出しを同期の代わりに並列で実行してサイトの応答性を向上させることです。現在、すべてのAPI呼び出しが互いにブロックしています。したがって、1ページに4回の通話が必要な場合は...ロード時間が長くなります。MVC4でAsync/Awaitが一度も応答しない

私は、すべてのAPI呼び出しの同期バージョンと非同期バージョンの両方に対して個別のメソッドを構築しました。私が持っている問題は、決して応答しないということです。私はそれがthis questionに関連していると思います。しかし、私は本当にそれを解決する方法がわかりません。私はConfigureAwait(false)を試しましたが、それは私を助けませんでした。

ここでは、コードです:

それはそうのように見えるコントローラでの最初の呼び出し:

BaseData bdata = API.GetBaseData().Result; 

た私たちは、私がここで待っ使用してみたいが、それはAsyncControllerなしオプションではないのですが、要求/応答アクセスが必要なために使用できません。これらのメソッドが呼び出されたとき、私は(GetYearsAsyncにブレークポイントを置くことができる

internal static async Task<BaseData> GetBaseData() { 
    var catTask = GetParentCategoriesAsync(); 
    var yearTask = GetYearsAsync(); 

    await Task.WhenAll(new Task[] { catTask, yearTask }); 

    var bdata = new BaseData { 
     years = await yearTask, 
     cats = await catTask 
    }; 
    return bdata; 
} 

internal static async Task<List<APICategory>> GetParentCategoriesAsync() { 
    try { 
     WebClient wc = new WebClient(); 
     wc.Proxy = null; 

     string url = getAPIPath(); 
     url += "GetFullParentCategories"; 
     url += "?dataType=JSON"; 

     Uri targeturi = new Uri(url); 
     List<APICategory> cats = new List<APICategory>(); 

     var cat_json = await wc.DownloadStringTaskAsync(targeturi); 
     cats = JsonConvert.DeserializeObject<List<APICategory>>(cat_json); 

     return cats; 
    } catch (Exception) { 
     return new List<APICategory>(); 
    } 
} 

internal static async Task<List<double>> GetYearsAsync() { 
    WebClient wc = new WebClient(); 
    wc.Proxy = null; 
    Uri targeturi = new Uri(getAPIPath() + "getyear?dataType=JSON"); 
    var year_json = await wc.DownloadStringTaskAsync(targeturi); 
    List<double> years = JsonConvert.DeserializeObject<List<double>>(year_json); 
    return years; 
} 

)とGetParentCategoriesAsync():他の方法は、APIのクラスです。 wc.DownloadStringTaskAsync(targeturi)コマンドを待つまですべてが起動します。それが停止場所です。

私はConfigureAwait(continueOnCapturedContext:false)をすべてのタスクに追加しましたが、それは助けにはなりませんでした。問題は、スレッドが同じコンテキストにないということです。しかし、私は確信していません。しかし、私は何か間違っていると確信しています。私は何がわかりません。それはどちらか、または私は.NET MVC4で行うことができないことをやろうとしています。どんな考えも高く評価されます。

+5

"すべての方法を非同期にする"ことができない場合は、非同期メソッドを使用するという点はあまりありません。トップレベルのタスクをブロックしようとしている場合は、操作の全期間にわたってスレッドプールスレッドをビジー状態にしています。非同期メソッドを使用するだけでよいでしょう。 – Servy

+0

バックグラウンドで長時間実行される操作を実行する場合は、HTTP要求を超えたサービスが必要です。 –

+3

"リクエスト/レスポンスアクセスが必要なために使用できない" - これが何を意味するのかはっきりしていませんか? 'async'コードは要求と応答にアクセスできます。 –

答えて

0

私はServy'sとStephen Clearyの助言の組み合わせに続いてしまった。スティーブンの応答に基づいて、コントローラーで非同期呼び出しを行う前に、要求/応答にアクセスできることを認識しました。

変数にHttpContextを格納した場合、そのコンテキストにアクセスする必要のあるすべてのモデル/サービスメソッドにそのコンテキストを渡すことができます。これにより、私はServyが提案したように非同期にしていくことができました。その後、非同期パターンでは何の問題もありませんでした。

+2

変数に 'HttpContext'を渡すべきでない他のスレッドに' HttpContext'を渡すことは、お勧めできません。 'async'と' await'( 'ConfigureAwait'なし)を使うだけで、* awaitの前と*の後の両方で' HttpContext.Current'にアクセスすることができます。 –

+0

私が最初に非公開の古い非同期/待機で最初に実行していた大きな問題は、HttpContext.CurrentがCookieデータを取得して設定する必要のあるヘルパーメソッドで空であることでした。 asyncとawaitだけで、HttpContext.Currentにアクセスしようとすると、どのメソッド呼び出しでもnull参照エラーがスローされます。変数にHttpContext.Currentを格納することによって、私は他の言語でモデル化されたのと同じように、私はそれを渡すことができます。変数として保存しないと、クッキーロジックはまったく機能しません。私はまだそこにいない何かがなければならないと思う。 – janiukjf

+0

これは別の問題です。なぜ 'HttpContext.Current'が' null'なのかという疑問をもって最小限のreproを投稿することをお勧めします。 –

2

問題は、実際にどのバック(原因Resultコールにブロックされている)要求コンテキストへ常に同期しますWebClientによるものです。

あなたはConfigureAwait(false)と組み合わせて、代わりにHttpClientを使用することができます。

のような、それを呼び出すことができますする必要があり
internal static async Task<BaseData> GetBaseDataAsync() { 
    var catTask = GetParentCategoriesAsync(); 
    var yearTask = GetYearsAsync(); 

    await Task.WhenAll(catTask, yearTask).ConfigureAwait(false); 

    var bdata = new BaseData { 
    years = await yearTask, 
    cats = await catTask 
    }; 
    return bdata; 
} 

internal static async Task<List<APICategory>> GetParentCategoriesAsync() { 
    try { 
    var client = new HttpClient(); 

    string url = getAPIPath(); 
    url += "GetFullParentCategories"; 
    url += "?dataType=JSON"; 

    Uri targeturi = new Uri(url); 
    List<APICategory> cats = new List<APICategory>(); 

    var cat_json = await client.GetStringAsync(targeturi).ConfigureAwait(false); 
    cats = JsonConvert.DeserializeObject<List<APICategory>>(cat_json); 

    return cats; 
    } catch (Exception) { 
    return new List<APICategory>(); 
    } 
} 

internal static async Task<List<double>> GetYearsAsync() { 
    var client = new HttpClient(); 
    Uri targeturi = new Uri(getAPIPath() + "getyear?dataType=JSON"); 
    var year_json = await client.GetStringAsync(targeturi).ConfigureAwait(false); 
    List<double> years = JsonConvert.DeserializeObject<List<double>>(year_json); 
    return years; 
} 

を:

BaseData bdata = API.GetBaseDataAsync().Result; 

しかし、私はあなたのようにそれを呼び出すことを強くをお勧めしますそのようなもの:

BaseData bdata = await API.GetBaseDataAsync(); 

awaitの前後のコードは要求と応答のコンテキストにアクセスできます。

関連する問題