Asynchrone Web Service-Aufrufe über HTTP mit .NET Framework

Veröffentlicht: 22. Nov 2002 | Aktualisiert: 21. Jun 2004
Von Matt Powell

Beim Schreiben von Anwendungen, die einen Web Service in Anspruch nehmen, müssen Sie auf jeden Fall die Latenz in Betracht ziehen, die bei Aufrufen in einem Netzwerk entsteht. In bestimmten Situationen, besonders in einem privaten Netzwerk mit genügend Bandbreite, können Aufrufe in einer halben Sekunde oder weniger abgeschlossen sein und die Wartezeit spielt keine große Rolle. Wenn Sie aber Aufrufe an einen entfernten Ort im Internet senden oder Aufrufe mit sehr langer Verarbeitungszeit ausführen, müssen Sie berücksichtigen, wie sich überlange Verzögerungen auf Ihre Anwendung auswirken. Bei Microsoft® Windows® Forms-Anwendungen kann es zum Beispiel scheinen, als ob sie angehalten hätten, während sie darauf warten, dass ein Web Service einen Aufruf zurückgibt. Wenn Sie einen Web Service von einer Microsoft® ASP.NET-Seite aufrufen, werden Sie vermutlich feststellen, dass mehrere Web Service-Aufrufe die Seitenanzeige um ein Vielfaches verlangsamen. Bei einer Anwendung mit vielen Web Service-Aufrufen ist es wichtig, die Aufrufe so effizient wie möglich auszuführen.

Die Lösung für viele dieser Probleme ist, die Aufrufe für Ihren Web Service asynchron auszuführen. Asynchrone Aufrufe werden sofort zurückgegeben und verwenden dann einen anderen Mechanismus, um anzugeben, wann der Aufruf tatsächlich abgeschlossen wird. Die Anwendung wird hierdurch frei für andere Aktivitäten, wie beispielsweise notwendige Hintergrundverarbeitung. Des Weitern Reaktionen auf Benutzereingaben an der Benutzeroberfläche, Feedback über den aktuellen Status der Anfrage oder sogar die Initialisierung von weiteren Web Service-Aufrufen. Wir werden uns ansehen, wie Microsoft® .NET Framework asynchrone Web Service-Aufrufe über HTTP unterstützt und wie wir diese in einigen häufigen Szenarios verwenden können.

Auf dieser Seite

Die Bausteine Die Bausteine
Drei Optionen zum Ausführen asynchroner Aufrufe Drei Optionen zum Ausführen asynchroner Aufrufe
Komplexe Szenarios Komplexe Szenarios
Verwenden von "asyncState" Verwenden von "asyncState"
Abfangen von Fehlern Abfangen von Fehlern
Abbrechen von Anforderungen Abbrechen von Anforderungen
Erstellen von Threads zum Ausführen von synchronen Aufrufen Erstellen von Threads zum Ausführen von synchronen Aufrufen
Schlussfolgerung Schlussfolgerung

Die Bausteine

Wenn Sie in Microsoft Visual Studio® .NET die Option Webverweis hinzufügen wählen, wird eine Klasse erstellt, die von System.Web.Services.Protocols.SoapHttpClientProtocol erbt. SoapHttpClientProtocol hat eine geschützte Funktion namens Invoke. Diese wird verwendet, wenn Sie eine der Methoden aufrufen, die von einem Web Service offen gelegt werden. Der Assistent legt für jede Webmethode, die in der WSDL des Web Service definiert ist, eine Funktion mit den entsprechenden Namensparametern und Rückgabewerten an. Jede dieser Funktionen ruft dann die Invoke-Funktion der SoapHttpClientProtocol-Klasse auf und übergibt die Parameter. Für diesen Artikel habe ich einen Web Service mit einer Methode erstellt, bei der es mit Absicht lange dauern kann, bis sie zurückgegeben wird. Die Webmethode heißt DelayedResponse, ihr einziger Parameter ist eine ganze Zahl und sie gibt eine Zeichenfolge zurück. Die Option Webverweis hinzufügen generiert für diese Methode den folgenden Proxycode:

Public Function DelayedResponse(ByVal waitPeriod As Integer) _
        As String
    Dim results() As Object _
        = Me.Invoke("DelayedResponse", _
                    New Object() {waitPeriod})
    Return CType(results(0),String)
End Function

Die Invoke-Methode benötigt zwei Parameter: den Funktionsnamen und ein Objektarray mit den Parametern, die an die Funktion übergeben werden. Die Invoke-Methode gibt ein Objektarray zurück, das in unserem Fall nur ein Element enthält - die Zeichenfolge, die von unserer Funktion zurückgegeben wurde. Dies ist der Mechanismus zum Ausführen synchroner Aufrufe, die erst zurückgegeben werden, wenn die Antwort empfangen wurde.

Die SoapHttpClientProtocol-Klasse hat auch eine Methode mit dem Namen BeginInvoke. Dies ist der Mechanismus, der eine asynchrone Anfrage startet. Die Klasse, die von Webverweis hinzufügen erstellt wurde, erstellt auch eine öffentliche Funktion BeginDelayedResponse zusätzlich zur blockierenden DelayedResponse-Funktion, die wir bereits beschrieben haben. Unten finden Sie den Code für BeginDelayedResponse.

Public Function BeginDelayedResponse( _
        ByVal waitPeriod As Integer, _
        ByVal callback As System.AsyncCallback, _
        ByVal asyncState As Object) As System.IAsyncResult
   Return Me.BeginInvoke("DelayedResponse", _
                         New Object() {waitPeriod}, _
                         callback, _
                         asyncState)
End Function

BeginDelayedResponse verwendet die BeginInvoke-Methode, die in vielen Aspekten der Invoke-Methode ähnelt, die wir bereits verwendet haben. Die ersten beiden Parameter sind mit den für die Invoke-Methode verwendeten identisch. Es gibt allerdings zwei weitere Parameter für BeginInvoke und es wird kein Objektarray mehr zurückgegeben. Der wesentliche Unterschied ist allerdings, dass BeginInvoke sofort zurückgegeben wird und nicht darauf wartet, dass der Web Service-Aufruf abgeschlossen wird.

Wenn wir den ersten der beiden zusätzlichen Parameter betrachten, BeginInvoke, sehen wir etwas, das System.AsyncCallback heißt. Dies ist ein so genannter Delegat, im Grunde ein Mechanismus, um einen Funktionszeiger in verwaltetem Code zu deklarieren. In diesem Fall wird die Funktion aufgerufen, wenn der Aufruf der Webmethode abgeschlossen ist und wir die Antwort erhalten haben.

Der letzte Parameter für BeginInvoke wird asyncState genannt und ist einfach als Objekttyp deklariert. Dies kann alles sein, womit Sie diesen Aufruf verfolgen möchten. Sie können die gleiche Rückruffunktion für viele verschiedene asynchrone Abfragen verwenden. Des Weiteren können Sie, um die Antworten auf die Aufrufe zu unterscheiden, Informationen über den Aufruf in den asyncState-Parameter einfügen. Diese Informationen stehen Ihnen in der Rückruffunktion zur Verfügung.

Der letzte Unterschied bei BeginInvoke ist, was zurückgegeben wird. Wenn der Aufruf noch nicht beendet ist, kann er natürlich keine Antwortdaten zurück an die Webmethode übermitteln. Er gibt einen System.IAsyncResult-Schnittstellenzeiger zurück. Durch diesen Schnittstellenzeiger können Sie Informationen über den Aufruf erhalten. IAsyncResult zeigt vier öffentliche Eigenschaften an, die im Folgenden aufgelistet sind:

Eigenschaft

Beschreibung

AsyncState

Dies sind einfach die Daten, die im vierten Parameter der BeginInvoke-Methode übergeben werden.

AsyncWaitHandle

Dies ist ein WaitHandle-Objekt, das verwendet werden kann, um die aktuelle Threadausführung zu blockieren, bis einer oder mehrere Web Service-Aufrufe abgeschlossen sind.

CompletedSynchronously

Dieser Parameter trifft auf Web Service-Aufrufe nicht zu. Die IAsyncResult-Schnittstelle wird für eine Reihe von E/A-Vorgängen verwendet und diese Eigenschaft würde Sie darüber informieren, ob die asynchrone E/A-Vorgangsabfrage so schnell abgeschlossen wurde, dass sie bereits vor der Rückgabe der Begin-Funktion beendet war.

IsCompleted

Dieses Flag können Sie verwenden, um zu bestimmen, ob der Aufruf abgeschlossen wurde oder nicht.

Letzten Endes ermöglicht es der IAsyncResult-Zeiger, dass Sie und das System eine asynchrone Beendigung von der anderen unterscheiden können. Er stellt auch verschiedene Optionen zur Verfügung, um zu definieren, wann ein Aufruf abgeschlossen ist. Auf die Verwendung dieser Optionen gehen wir in Kürze ein.

Wie bereits erwähnt, erstellt die Option Webverweis hinzufügen eine Funktion für jede von Ihrem Dienst bereitgestellte Webmethode, die BeginInvoke verwendet. In unserem Fall ist die generierte Methode BeginDelayedResponse. Diese Methode ist ein Wrapper für die BeginInvoke-Methode und zeigt die Parameter für den Webmethodenaufruf und die Rückruf- und asyncState-Parameter an, die von BeginInvoke zur Verfügung gestellt werden. Als Nächstes betrachten wir die drei Möglichkeiten, eine asynchrone Abfrage auszuführen und zu definieren, wann sie abgeschlossen ist.

Drei Optionen zum Ausführen asynchroner Aufrufe

Jede Anwendung ist anders, und es gibt Szenarios zum Ausführen asynchroner Aufrufe, die in einigen Anwendungen funktionieren und in anderen nicht. .NET Framework bietet flexible Möglichkeiten, asynchrone Webaufrufe auszuführen. Sie können ein Polling ausführen, um festzustellen, wann der Aufruf beendet ist, mit WaitHandle blockieren, oder auf die Rückruffunktion warten. Lassen Sie uns auf diese drei Ansätze eingehen.

Polling zum Abfragen der Beendigung

Die IAsyncResult-Schnittstelle, die von unserer BeginDelayedResponse-Funktion zurückgegeben wird, verfügt über eine IsCompleted-Eigenschaft. Diese kann geprüft werden, um festzustellen, ob die Abfrage abgeschlossen ist oder nicht. Sie können diese Eigenschaft solange abfragen, bis sie den Wert TRUE zurückgibt. Unten sehen Sie gekürzt den Code, der diese Vorgehensweise zeigt:

' Pollingcode, der den Prozessor auslasten kann
Dim proxy as New localhost.Service1()
Dim result as IAsyncResult
Result = proxy.BeginDelayedResponse(2000, _
                                    Nothing, _
                                    Nothing)
While (result.IsCompleted = False)
    ' Verarbeiten
        ...
Wend
Dim response as String
response = proxy.EndDelayedResponse(result)

Polling ist eine unkomplizierte Lösung, sie hat aber einige Nachteile. Der Code zeigt das Ausführen des Initialaufrufs von BeginDelayedResponse, mit 2000 als Übergabeparameter an die Webmethode. Rückruf und asyncState werden dann auf Nothing gesetzt. Es folgt eine While-Schleife, die die IsCompleted-Eigenschaft abfragt, bis sie wahr (TRUE) ist. Wenn der Aufruf abgeschlossen und die IsCompleted-Eigenschaft TRUE ist, verlassen wir die While-Schleife und erhalten die Antwort durch eine weitere Funktion in der durch Webverweis hinzufügen generierten Klasse, EndDelayedResponse. EndDelayedResponse ist eine Wrapperfunktion für die EndInvoke-Methode der SoapHttpClientProtocol-Klasse und dient als Mechanismus dafür, die zurückgegebenen Daten vom Webmethodenaufruf zu erhalten. EndDelayedResponse wird verwendet, wenn bekannt ist, dass der Web Service-Aufruf abgeschlossen ist, und gibt einfach die gleichen Informationen zurück, die die Invoke-Methode für blockierende Aufrufe zurücksendet. Wir werden die EndDelayedResponse-Methode in allen drei asynchronen Szenarios verwenden, um die Resultate von unserem Web Service-Aufruf zu erhalten. Es ist wissenswert, dass beim Aufrufen von EndDelayedResponse vor dem Abschluss der Anforderung einfach blockiert wird, bis die Anforderung abgeschlossen ist.

Eines der Probleme, die Sie bei dem erläuterten Polling beachten müssen, ist, dass Sie eventuell sehr viel von der Rechenleistung Ihres Computers in Anspruch nehmen, wenn Sie nicht sorgfältig darauf achten. Wenn beispielsweise kein Code in der While-Schleife existiert, kann der Thread, der den Code ausführt, einen Großteil der Prozessorleistung Ihres Computers verschlingen. Tatsächlich kann so viel Prozessorzeit verbraucht werden, dass der zugrunde liegende Code für das Senden unserer Web Service-Abfrage und das Erhalten der Antwort verzögert werden kann. Sie sollten daher vorsichtig damit sein, wie Sie Polling verwenden.

Wenn Sie wirklich nur auf die Beendigung der Web Service-Anforderung warten wollen, sollten Sie eher die WaitHandle-Lösung einsetzen, der wir uns jetzt widmen. Wenn Sie allerdings viel verarbeiten müssen und nur ab und zu überprüfen möchten, ob der Web Service-Aufruf beendet ist, stellt die Verwendung von Polling keine schlechte Lösung dar. Häufig kommt bei Anwendungen eine Kombination aus Polling und einer der anderen asynchronen Vorgehensweisen zum Einsatz. Sie können den Web Service-Aufruf zum Beispiel asynchron durchführen, weil Sie eine Hintergrundverarbeitung ausführen. Sobald Sie diese aber beendet haben, möchten Sie blockieren, bis der Web Service beendet ist. In diesem Fall verwenden Sie Polling gelegentlich während der Verarbeitung und dann WaitHandle, um auf die Antwort zu warten, sobald die Hintergrundverarbeitung beendet ist.

Verwenden von WaitHandles

WaitHandles sind geeignet für Szenarios, bei denen Sie asynchrone Aufrufe durchführen müssen, Ihren gegenwärtig ausgeführten Thread aber nicht freigeben wollen. Wenn Sie beispielsweise aus einer ASP.NET-Anwendung heraus einen asynchronen Web Service-Aufruf ausführen und dann von dem Ereignis zurückkehren, das Sie für Ihre ASP.NET-Anwendung verarbeiten, haben Sie eventuell nicht die Möglichkeit, die Daten aus dem Web Service-Aufruf in die Daten einzuschließen, die an den Benutzer zurückgegeben werden. Mit WaitHandles können Sie nach Ihrem Web Service-Aufruf Verarbeitungen ausführen und dann blockieren, bis der Web Service-Aufruf beendet ist. WaitHandles sind besonders nützlich, wenn Sie mehrfache Web Service-Aufrufe aus einer ASP.NET-Seite heraus ausführen.

Der Zugriff auf ein WaitHandle-Objekt erfolgt durch das von der BeginDelayedResponse-Funktion zurückgegebene IAsyncResult. Der folgende Code zeigt ein einfaches Szenario, das WaitHandle verwendet.

' Einfacher WaitHandle-Code 
Dim proxy As New localhost.Service1()
Dim result As IAsyncResult
result = proxy.BeginDelayedResponse(2000, Nothing, Nothing)
'  Verarbeiten 
'          
'  Verarbeitung beendet.  Auf Beendigung warten.
result.AsyncWaitHandle.WaitOne()
Dim response As String
response = proxy.EndDelayedResponse(result)

In diesem Code haben wir die WaitOne-Methode des WaitHandle-Objekts verwendet, um auf einen Handle zu warten. Die WaitHandle-Klasse verfügt auch über die statischen Methoden WaitAll und WaitAny. Diese zwei statischen Methoden verwenden WaitHandle-Arrays als Parameter und werden entweder zurückgegeben, wenn alle Aufrufe beendet sind, oder sobald einer der Aufrufe vollständig ist, abhängig davon, welche Funktion Sie aufrufen. Nehmen wir an, Sie führen drei verschiedene Web Service-Aufrufe aus. Sie können alle asynchron aufrufen, WaitHandle für jeden in einem Array platzieren und dann die WaitAll-Methode aufrufen, bis sie beendet sind. Auf diese Weise können die Web Service-Aufrufe gleichzeitig ausgeführt werden. Würden Sie dies synchron durchführen, könnten Sie die Aufrufe nicht parallel ausführen und bräuchten ungefähr die dreifache Zeit.

Die Methoden WaitOne, WaitAll und WaitAny akzeptieren alle einen Timeout als Parameter. Dadurch können Sie kontrollieren, wie lange Sie auf die Beendigung von einem Web Service warten möchten. Bei einer Zeitüberschreitung geben die Methoden den Wert FALSE zurück. Auf diese Weise können Sie weitere Verarbeitungen durchführen, bevor Sie erneut warten, oder Sie haben die Möglichkeit, die Abfragen abzubrechen.

Verwenden von Rückrufen

Das dritte Verfahren für asynchrone Web Service-Aufrufe ist das Verwenden von Rückrufen. Bei vielen gleichzeitigen Web Service-Aufrufen kann es sehr effizient sein, Rückrufe zu verwenden. Diese Lösung ist gut geeignet für die Hintergrundverarbeitung der Ergebnisse von Web Service-Aufrufen. Dieser Ansatz ist allerdings komplexer als die anderen beiden Vorgehensweisen. Rückrufe in einer Microsoft Windows®-Anwendung zu verwenden ist besonders vorteilhaft, da auf diese Weise der Nachrichtenthread für Ihr Fenster nicht blockiert wird.

Rückrufe laufen einfach darauf hinaus, dass die Rückruffunktion aufgerufen wird, wenn der Web Service-Aufruf abgeschlossen ist. Die Rückruffunktion wird eventuell aber nicht im Kontext des Threads aufgerufen, der den ursprünglichen BeginInvoke-Aufruf ausgeführt hat. Dadurch können Probleme beim Senden von Befehlen an Steuerelemente in einer Windows Form-Anwendung entstehen, da diese Befehle von genau dem Thread aufgerufen werden müssen, der die Nachrichtenverarbeitung für dieses spezielle Fenster erledigt. Glücklicherweise kann man dieses Problem umgehen.

Der folgende Code stellt ein einfaches Szenario dar, in dem ein Rückruf verwendet wird und die Ergebnisse des Web Service-Aufrufs in einem Feldsteuerelement angezeigt werden.

Dim proxy as localhost.Service1
Private Delegate Sub MyDelegate(ByVal response As String)
Private Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button1.Click
    proxy = New localhost.Service1()
    proxy.BeginDelayedResponse(2000, _
            New AsyncCallback(AddressOf Me.ServiceCallback), _
            Nothing)
End Sub
Private Sub ServiceCallback(ByVal result As IAsyncResult)
    Dim response As String
    response = proxy.EndDelayedResponse(result)
    Label1.Invoke( _
        New MyDelegate(AddressOf Me.DisplayResponse), _
        New Object() {response})
End Sub
Private Sub DisplayResponse(ByVal response As String)
    Label1.Text = response
End Sub

Dieser Code ruft BeginDelayedResponse im Button1_Click-Ereignis auf und kehrt dann von der Subroutine zurück. Wie Sie sehen, übergeben wir als zweiten Parameter ein AsyncCallback-Objekt statt Nothing. Dies ist im Grunde nur ein Weg, um unsere Rückruffunktion ServiceCallback zu verpacken. Die ServiceCallback-Funktion muss eine Subroutine sein, die einen einzigen Parameter vom Typ IAsyncResult erfordert. Die SoapHttpClientProtocol-Klasse ruft diese Funktion auf und stellt den IAsyncResult-Schnittstellenzeiger zur Verfügung, der dem abgeschlossenen Web Service-Aufruf entspricht. Wir verwenden auch diesmal die EndDelayedResponse-Methode, um die Ergebnisse zu erhalten, aber hier haben wir ein kleines Problem.

Unser Rückruf ist wahrscheinlich nicht im Hauptthread für unser Fenster, und wir müssen daher eine andere Invoke-Methode anwenden, die uns ermöglicht, den Text für ein Feld in unserem Fenster einzufügen. Alle Steuerelemente verfügen über eine Invoke-Methode, die Sie aufrufen können, um eine Funktion aufzurufen, die im Hauptnachrichtenthread des Steuerelements ausgeführt wird. Die Verwendung von Invoke bei einem Steuerelement entspricht anderen Methoden, die wir bereits besprochen haben. Wir geben Invoke die Adresse der Funktion, die wir aufrufen möchten. Danach schließen wir alle Parameter, die wir dieser Funktion übergeben möchten, als zweiten Parameter in ein Objektarray ein. Wir müssen in diesem Fall einen Delegattyp deklarieren, den wir MyDelegate nennen, um die Invoke-Methode über die Syntax der Funktion zu informieren, die wir aufrufen möchten. In diesem Beispiel heißt die Funktion DisplayResponse und hat nur einen Parameter: die Zeichenfolge, die vom Web Service-Aufruf zurückgegeben wird. DisplayResponse wird im richtigen Thread für das label1-Steuerelement aufgerufen und wird ohne Probleme den Text des Steuerelements für unsere Anwendung einfügen.

Komplexe Szenarios

Bis jetzt waren die aufgeführten Beispiele ziemlich unkompliziert. Sie haben vieles vorausgesetzt, beispielsweise den Erfolg unserer Web Service-Aufrufe und sind dadurch vereinfacht, dass zu jeder Zeit jeweils nur ein einzelner Aufruf stattfindet. Jetzt wenden wir uns einem Szenario mit mehreren Web Service-Aufrufen zu. Wir behandeln die Verarbeitung von Fehlern, die möglicherweise von den Aufrufen erzeugt werden, und den ggf. nötigen Abbruch von Aufrufen.

Verwenden von "asyncState"

Wir müssen noch ein Beispiel betrachten, bei dem wir etwas anderes als Nothing als letzten Parameter an unseren BeginDelayedResponse-Aufruf übergeben. Wie Sie sich erinnern, ist dies der asyncState-Parameter, der einfach als Objekt deklariert ist. Wir werden diesen Parameter jetzt verwenden, um mehrere Web Service-Aufrufe zu ermöglichen, so dass wir die Antworten den entsprechenden Anfragen zuordnen können.

Ich möchte in einem Szenario drei verschiedene Aufrufe der DelayedResponse-Webmethode ausführen. Die Ergebnisse dieser Aufrufe werden dann in drei verschiedenen Feldsteuerelementen in meiner Windows-Anwendung angezeigt. Das Ergebnis meines ersten Aufrufs soll im ersten Feld, das des zweiten im zweiten Feld und das des dritten im dritten Feld angezeigt werden. Für alle Web Service-Aufrufe verwende ich die gleiche Rückruffunktion. Um zu definieren, welches Feld (Label) zu welchem Aufruf gehört, übergebe ich im asyncState-Parameter das Label-Objekt. Um das Ganze etwas verwirrender zu gestalten, habe ich die Länge der Zeit, die die einzelnen Aufrufe bis zum Beenden benötigen, zufällig gewählt. Der entsprechende Code wird unten gezeigt.

Dim proxy As localhost.Service1
Private Delegate Sub LabelDelegate( _
    ByVal responseLabel As Label, _
    ByVal response As String)
Private Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button7.Click
    proxy = New localhost.Service1()
    ClearForm()
    Dim r As New Random()
    proxy.BeginDelayedResponse(r.Next(1, 10000), _
            New AsyncCallback(AddressOf Me.ServiceCallback), _
            Label1)
    proxy.BeginDelayedResponse(r.Next(1, 10000), _
            New AsyncCallback(AddressOf Me.ServiceCallback), _
            Label2)
    proxy.BeginDelayedResponse(r.Next(1, 10000), _
            New AsyncCallback(AddressOf Me.ServiceCallback), _
            Label3)
End Sub
Private Sub ServiceCallback(ByVal result As IAsyncResult)
    Dim response As String
    response = proxy.EndDelayedResponse(result)
    Dim responseLabel As Label = result.AsyncState
    responseLabel.Invoke( _
        New LabelDelegate(AddressOf Me.DisplayResponses), _
        New Object() {responseLabel, response})
End Sub
Private Sub DisplayResponses(ByVal responseLabel As Label, _
                             ByVal response As String)
    responseLabel.Text = response
    Form1.ActiveForm.Refresh()
End Sub

Das Label-Objekt, das im asyncState-Parameter übergeben wurde, ist in unserer Rückruffunktion durch den IAsyncResult-Parameter verfügbar, der an den Rückruf übergeben wird. Der Delegat aus dem früheren Beispiel wurde durch einen weiteren Parameter geändert. Der zusätzliche Parameter ist wieder das Feld, so dass der Delegat weiß, für welches Steuerelement der Text bestimmt ist.

Sie werden häufig mehr Informationen als nur ein einzelnes Objekt durch asyncState übergeben wollen. Da asyncState nur als Objekt deklariert ist, kann es auch komplexe Informationen weitergeben, wie zum Beispiel ein Objektarray oder eine komplexe Struktur, die Sie verwenden möchten. Wir verwenden hier nur ein Proxyobjekt. In bestimmten Szenarios kann es aber für jeden Web Service-Aufruf ein anderes Proxyobjekt geben. In diesem Fall sollten Sie auch das Proxyobjekt in die asyncState-Daten einschließen. Auch alle anderen Daten, die für Ihren Web Service spezifisch sind, können hinzugefügt werden.

Abfangen von Fehlern

Wenn Sie synchrone Web Service-Aufrufe einsetzen, verwenden Sie zum Abfangen der Fehler wahrscheinlich auch try...catch-Blöcke, die den Remoteaufruf einschließen. Bei asynchronen Aufrufen genauso vorzugehen, erscheint vielleicht verwirrend. Schließe ich BeginDelayedResponse oder EndDelayedResponse in einen try.catch-Block ein? Vielleicht sogar beide!

Tatsächlich müssen wir für normale SOAP-Fehler und andere transportbedingte Fehler nur den EndDelayedResponse-Aufruf in einen try.catch-Block einschließen. Da BeginDelayedResponse sofort zurückgegeben wird und nicht auf Probleme wartet, können wir davon ausgehen, dass hier kein Fehler verursacht wird. Fehler passieren nicht zufällig, während man auf den Aufruf von Rückrufen wartet oder darauf, dass Wait-Aufrufe nicht mehr blockiert sind. Was bei einem Fehler passiert, ist Folgendes: Eine Beendigung wird durch den von Ihnen verwendeten Mechanismus ausgelöst. Das kann durch das Setzen der IsCompleted-Eigenschaft auf TRUE geschehen, durch das Auslösen von WaitHandle oder durch einen Rückruf. Nur wenn Sie EndDelayedResponse aufrufen, wird ein Fehler ausgelöst, der Informationen darüber enthält, was nicht funktioniert haben könnte.

Um die Möglichkeit von Fehlern zu berücksichtigen, habe ich meiner Rückruffunktion einen try...catch-Block hinzugefügt, der EndDelayedResponse umgibt. Den Antworttext habe ich einem Fehler entsprechend umformuliert.

Private Sub ServiceCallback(ByVal result As IAsyncResult)
    Dim response As String
    Try
        response = proxy.EndDelayedResponse(result)
    Catch e As Exception
        Response = "Fehler"
    End Try
    Dim responseLabel As Label = result.AsyncState
    responseLabel.Invoke( _
        New LabelDelegate(AddressOf Me.DisplayResponses), _
        New Object() {responseLabel, response})
End Sub

Abbrechen von Anforderungen

Einer der Vorteile der Verwendung von asynchronen Anforderungen in einer Anwendung ist, dass Sie nicht Ihre Benutzeroberfläche sperren müssen, während ein Aufruf abgeschlossen wird. Wenn Sie den Benutzern Ihrer Anwendung ermöglichen, die Anwendung weiter anzusprechen, wird eine häufig verwendete Funktion natürlich eine Schaltfläche zum Abbrechen der ausstehenden Anforderungen sein. Es ist oft sinnvoll, die Benutzer definieren zu lassen, wie lange sie warten möchten, wenn ein Web Service-Aufruf lange dauert.

Eine Anforderung abzubrechen ist einfach. Verwenden Sie die Abort-Methode, die in der Proxyklasse verfügbar ist, diese wird beim Verwenden der Option Webverweis hinzufügen erstellt. Einige Punkte sollten Sie beim Abbrechen von Anforderungen beachten. Wenn Sie die Abort-Methode aufrufen, werden alle noch ausstehenden Funktionen zwar abgeschlossen, aber sie werden mit einem Fehler beendet. Wenn Sie Rückrufe verwenden, bedeutet dies, dass die Rückruffunktion für jede ausstehende Anforderung noch aufgerufen wird. Wenn die EndInvoke-Methode aufgerufen wird, oder wie in unserem Beispiel die EndDelayedResponse-Wrapperfunktion, wird ein Fehler generiert, der anzeigt, dass die zugrunde liegende Verbindung geschlossen wurde.

Erstellen von Threads zum Ausführen von synchronen Aufrufen

Es gibt eine weitere Möglichkeit zur Lösung vieler Probleme, mit denen sich asynchrone Aufrufe befassen: Man erstellt einfach einen Thread und lässt diesen Thread leicht zu codierende synchrone Aufrufe durchführen. Diese Vorgehensweise ist in manchen Szenarios sinnvoll, aber es müssen einige Schwierigkeiten beachtet werden.

Der Code für Ihren Web Service-Aufruf mag auf diese Weise zwar ein wenig vereinfacht werden, aber es ist sehr wahrscheinlich, dass Sie Logik implementieren müssen, die wenigstens so kompliziert ist wie der Code, den wir für die Verwaltung und die Kommunikation der Threads verwendet haben. Bei vielen Web Service-Aufrufen kann es passieren, dass Sie Ihr System mit der Verwaltung der Threads überlasten. Bei einer normalen Clientanwendung ist das halb so schlimm, aber wenn Sie Aufrufe von einer ASP.NET-Seite ausführen, müssen Sie bedenken, wie viele Benutzer das System eventuell gleichzeitig verwenden. Zwei weitere Threads, die Web Service-Aufrufe von einer ASP.NET-Seite ausführen, erscheinen zunächst nicht als großer Systemverwaltungsaufwand. Aber wie sieht es aus, wenn 200 andere Benutzer gleichzeitig dieselbe Seite verwenden? Jetzt handelt es sich um 400 neue Threads, was auf jeden Fall von Bedeutung ist. Der Rückrufmechanismus für asynchrone Aufrufe verwendet zwar zusätzliche Threads, um die Rückrufe auszuführen, dabei wird jedoch ein Pool von Threads wieder verwendet, um einerseits die Rückrufe so effizient wie möglich auszuführen und andererseits Probleme mit Hunderten von Threads zu vermeiden.

Wenn das Erstellen von Hintergrundthreads für Ihre Web Service-Aufrufe trotzdem Sinn macht, sollten Sie in Betracht ziehen, diese Threads mithilfe von Delegaten und dem Prozessthreadpool zu erstellen. Sie können dann diese Methoden aufrufen, indem Sie asynchrone Paradigmen ähnlich denen verwenden, die wir für Web Service-Rückrufe verwendet haben und für den Aufruf von Funktionsaufrufen, die über Threads mit Steuerelementen interagieren. Weitere Informationen über die Verwendung von Delegaten zum Aufrufen von allgemeinen asynchronen Methoden erhalten Sie in folgendem Artikel von Richard Grimes: .Net Delegates: Making Asynchronous Method Calls in the .Net Environment (in Englisch).

Schlussfolgerung

Das Ausführen von asynchronen Web Service-Aufrufen ist wahrscheinlich eine der sinnvollsten Anwendungsmöglichkeiten, wenn man einen Web Service über HTTP von .NET Framework-Anwendungen aus in Anspruch nimmt. Die meisten realen Anwendungen sollten diese Fähigkeit nutzen, um Web Service-Aufrufe effizient auszuführen, ohne dass Anwendungen durch die Ausführung von potenziell übermäßig langen Netzwerkaufrufen blockiert werden. .NET Framework ist flexibel in der Unterstützung von asynchronen Web Service-Aufrufen über HTTP und gibt den Entwicklern viel Entscheidungsfreiheit darüber, wie sie die Beendigungen behandeln.


Anzeigen: