2009-08-08 57 views
8

我有一個ASP.NET MVC應用程序,當前使用WebClient類從控制器操作中對外部Web服務進行簡單調用。異步使用ASP.NET MVC內的WebClient?

目前我正在使用同步運行的DownloadString方法。我遇到了外部Web服務無響應的問題,這導致我的整個ASP.NET應用程序處於線程飢餓和無響應狀態。

解決此問題的最佳方法是什麼?有一個DownloadStringAsync方法,但我不確定如何從控制器調用它。我需要使用AsyncController類嗎?如果是這樣,AsyncController和DownloadStringAsync方法如何交互?

感謝您的幫助。

回答

7

我認爲使用AsyncControllers將幫助你在這裏,因爲他們卸載請求線程的處理。

我會用這樣的事情(使用作爲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異步調用添加超時? – 2011-04-19 15:55:39

+1

只是一個說明,看起來這個答案在09年發佈在MVC 1.0的日子裏。現在使用MVC 2/3,答案略有不同。 MapAsyncRoute方法已消失,不再需要。此外,MyAction方法現在需要重命名爲MyActionAsync。否則,一切都以同樣的方式工作。 – BFree 2011-06-16 21:41:57

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

我相信這種做法仍然會阻塞請求的線程,雖然 - 你自己的睡眠通話過程中仍然是我的請求線程上。 異步控制器將在這裏幫助,因爲它們在等待外部資源時釋放請求線程。 – 2009-08-09 04:38:54

+0

@邁克確實。這種方法將與請求線程並行運行,除非WebClient請求在控制器線程完成其他所有需要做的時間之前未完成 - 此時控制器選擇是否阻止/休眠或繼續。我沒有得到這樣的印象,即問題是如何使用AsyncController,而是如何使WebClient異步工作。這可能是對這個問題的錯誤解讀。 – 2009-08-09 05:00:58

+0

@Rex是的,我認爲從Web應用程序的描述是「線程匱乏且無響應」,可能是OP請求線程不足。將外部響應限制在3秒內會有所幫助(如果您的外部請求比以前花費的時間更長),但這仍然需要很長時間才能保持線程,因此不會擴展。 – 2009-08-09 05:13:41