2009-08-08 7 views
8

私は現在、WebClientクラスを使用してコントローラアクション内から外部Webサービスを簡単に呼び出すASP.NET MVCアプリケーションを使用しています。ASP.NET MVC内でWebClientを非同期で使用していますか?

現在、私は同期で実行さDownloadString方法を、使用しています。私は、外部Webサービスが応答しない問題にぶつかりました。その結果、ASP.NETアプリケーション全体がスレッド不足になり、応答しなくなりました。

この問題を解決する最良の方法は何ですか? DownloadStringAsyncメソッドがありますが、コントローラから呼び出す方法が不明です。 AsyncControllerクラスを使用する必要がありますか?もしそうなら、AsyncControllerメソッドとDownloadStringAsyncメソッドはどのように相互作用しますか?

ありがとうございました。

答えて

7

AsyncControllerを使用すると、リクエストスレッドからの処理をオフロードする際に役立ちます。

私は(this articleで説明したようにイベントパターンを使用して)このようなものを使用したい:

public class MyAsyncController : AsyncController 
{ 
    // The async framework will call this first when it matches the route 
    public void MyAction() 
    { 
     // Set a default value for our result param 
     // (will be passed to the MyActionCompleted method below) 
     AsyncManager.Parameters["webClientResult"] = "error"; 
     // Indicate that we're performing an operation we want to offload 
     AsyncManager.OutstandingOperations.Increment(); 

     var client = new WebClient(); 
     client.DownloadStringCompleted += (s, e) => 
     { 
      if (!e.Cancelled && e.Error == null) 
      { 
       // We were successful, set the result 
       AsyncManager.Parameters["webClientResult"] = e.Result; 
      } 
      // Indicate that we've completed the offloaded operation 
      AsyncManager.OutstandingOperations.Decrement(); 
     }; 
     // Actually start the download 
     client.DownloadStringAsync(new Uri("http://www.apple.com")); 
    } 

    // This will be called when the outstanding operation(s) have completed 
    public ActionResult MyActionCompleted(string webClientResult) 
    { 
     ViewData["result"] = webClientResult; 
     return View(); 
    } 
} 

をとを確認してください(Global.asax.csで)例えば、あなたが必要とするものは何でもルートの設定、:

public class MvcApplication : System.Web.HttpApplication 
{ 
    public static void RegisterRoutes(RouteCollection routes) 
    { 
     routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

     routes.MapAsyncRoute(
      "Default", 
      "{controller}/{action}/{id}", 
      new { controller = "Home", action = "Index", id = "" } 
     ); 
    } 
} 
+0

WebClient非同期呼び出しにタイムアウトを追加する方法はありますか? –

+1

ちょっとメモしておけば、この回答はMVC 1.0で09年に掲載されたようです。 MVC 2/3では答えはちょっと違います。 MapAsyncRouteメソッドがなくなり、不要になりました。また、MyActionメソッドの名前をMyActionAsyncに変更する必要があります。それ以外の場合は、すべて同じように動作します。 – BFree

3

DownloadStringAsyncメソッドは、それが終了したときDownloadStringCompletedを上げ、イベントモデルを使用しています。 WebClient.CancelAsync()に電話することによって、時間がかかりすぎる場合はリクエストを停止することもできます。これにより、メインリクエストスレッドとWebClientスレッドを並行して実行できるようになり、メインスレッドが返す前にどれくらい待つかを正確に決めることができます。以下の例では

、我々は、ダウンロードを開始し、我々はそれが終了したときに呼び出さたいイベントハンドラを設定します。 DownloadStringAsyncはすぐに戻りますので、残りのリクエストを処理し続けることができます。 ;我々はコントローラのアクションの最後に到達するとき、我々はダウンロードがまだ完了しているかどうかを確認することができ、この操作をより細かく制御を実証するために

そうでない場合は、3秒以上待ってから中止してください。

string downloadString = null; 

ActionResult MyAction() 
{ 
    //get the download location 
    WebClient client = StartDownload(uri); 
    //do other stuff 
    CheckAndFinalizeDownload(client); 
    client.Dispose(); 
} 

WebClient StartDownload(Uri uri) 
{ 
    WebClient client = new WebClient(); 
    client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Download_Completed); 
    client.DownloadStringAsync(uri); 
    return client; 
} 

void CheckAndFinalizeDownload(WebClient client) 
{ 
    if(this.downloadString == null) 
    { 
     Thread.Sleep(3000); 
    } 
    if(this.downloadString == null) 
    { 
     client.CancelAsync(); 
     this.downloadString = string.Empty; 
    } 
} 

void Download_Completed(object sender, DownloadStringCompletedEventArgs e) 
{ 
    if(!e.Cancelled && e.Error == null) 
    { 
     this.downloadString = (string)e.Result; 
    } 
} 
+0

私は、このアプローチはまだかかわらず、要求のスレッドをブロックします信じている - あなたはあなたの睡眠の通話中に要求スレッドに残っています。 外部リソースを待っている間にリクエストスレッドを解放するため、非同期コントローラがここで役立ちます。 @Michael確かに –

+0

。このアプローチは、WebClient要求がコントローラスレッドが実行する必要があるすべての作業を完了するまでに完了しない限り、要求スレッドと並行して実行されます。コントローラはブロック/スリープまたは継続のどちらを選択するかを選択します。 AsyncControllerの使い方に関する質問ではなく、WebClientを非同期に動作させる方法についての印象はありませんでした。それは質問の誤った読みであるかもしれません。 –

+0

@Rexええ、私は、Webアプリの説明から「スレッドが枯渇し、応答がなくなっている」と思っています.OPのリクエストスレッドが不足している可能性が高いと思います。外部からの応答を3秒に制限することは、(あなたの外部要求が以前よりも長くかかっていた場合は)少し助けになりますが、それでもスレッドを保留するのにはかなり時間がかかり、したがってスケーリングにはならないでしょう。 –

関連する問題