2009-08-08 3 views
8

Ich habe eine ASP.NET MVC-Anwendung, die derzeit die WebClient-Klasse verwendet, um einen einfachen Aufruf an einen externen Webdienst innerhalb einer Controller-Aktion durchzuführen.WebClient in ASP.NET MVC asynchron verwenden?

Derzeit verwende ich die DownloadString-Methode, die synchron läuft. Ich bin auf Probleme gestoßen, bei denen der externe Webdienst nicht mehr reagiert, was dazu führt, dass meine gesamte ASP.NET-Anwendung nicht mehr funktioniert und nicht mehr reagiert.

Was ist der beste Weg, um dieses Problem zu beheben? Es gibt eine DownloadStringAsync-Methode, aber ich weiß nicht, wie ich diese vom Controller aufrufen soll. Muss ich die AsyncController-Klasse verwenden? Wenn ja, wie interagieren der AsyncController und die DownloadStringAsync-Methode?

Danke für die Hilfe.

Antwort

7

Ich denke, die Verwendung von AsyncControllern wird Ihnen hier helfen, da sie die Verarbeitung des Anforderungsthreads auslagern.

ich so etwas wie diese verwenden würde (mit dem Ereignismuster wie in this article beschrieben):

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(); 
    } 
} 

Und stellen Sie sicher, dass Sie Setup, was Routen, die Sie benötigen, zB (in 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

gibt es eine Möglichkeit, eine Zeitüberschreitung zu Webclient Async-Aufruf hinzuzufügen? –

+1

Nur eine Anmerkung, es scheint, diese Antwort wurde in 09 zurück in der MVC 1.0 Tage veröffentlicht. Jetzt mit MVC 2/3 ist die Antwort LEICHT anders. Die MapAsyncRoute-Methode ist nicht mehr erforderlich. Außerdem muss die MyAction-Methode jetzt in MyActionAsync umbenannt werden.Ansonsten funktioniert alles genauso. – BFree

3

Die DownloadStringAsync-Methode verwendet ein Ereignismodell, wodurch die DownloadStringCompleted ausgelöst wird, wenn sie beendet ist. Sie können die Anforderung auch stoppen, wenn sie zu lange dauert, indem Sie WebClient.CancelAsync() aufrufen. Dadurch können Ihr Hauptanfragen-Thread und Ihr WebClient-Thread parallel ausgeführt werden und Sie können genau entscheiden, wie lange Ihr Haupt-Thread warten soll, bevor er zurückkehrt.

Im folgenden Beispiel initiieren wir den Download und setzen den Event-Handler, der aufgerufen werden soll, wenn er fertig ist. Der DownloadStringAsync wird sofort zurückgegeben, sodass wir den Rest unserer Anfrage weiter bearbeiten können.

Um eine detailliertere Kontrolle über diesen Vorgang zu demonstrieren, können wir am Ende unserer Controller-Aktion prüfen, ob der Download bereits abgeschlossen ist. Wenn nicht, geben Sie ihm noch 3 Sekunden und brechen Sie dann ab.

+0

Ich glaube, dass dieser Ansatz den Anfrage-Thread immer noch blockieren wird - Sie sind immer noch auf dem Anfrage-Thread während Ihres Sleep-Anrufs. Async-Controller helfen hier, weil sie den Anfrage-Thread freigeben, während Sie auf die externe Ressource warten. –

+0

@Michael tatsächlich. Dieser Ansatz wird parallel zum Anforderungsthread ausgeführt, es sei denn, die WebClient-Anforderung wird nicht abgeschlossen, wenn der Steuerungsthread alle weiteren Aufgaben erledigt hat. Zu diesem Zeitpunkt entscheidet der Controller, ob er blockieren/schlafen oder fortfahren soll. Ich habe nicht den Eindruck, dass die Frage speziell darin besteht, wie man den AsyncController benutzt, sondern wie man den WebClient asynchron arbeiten lässt. Das könnte eine falsche Lesart der Frage sein. –

+0

@Rex Ja, ich denke, aus der Beschreibung der Web-App, die "Thread-verhungert und nicht reagierend" ist, ist es wahrscheinlich, dass das OP keine Request-Threads mehr hat. Die Begrenzung der externen Antwort auf 3 Sekunden hilft ein wenig (wenn Ihre externen Anfragen früher länger gedauert haben), aber das ist immer noch eine ziemlich lange Zeit, um den Thread hochzuhalten und daher würde er nicht skalieren. –