Communication asynchrone avec les services Web XML
La communication asynchrone avec un service Web XML suit le modèle de design asynchrone utilisé par le reste du Microsoft .NET Framework. Avant d'en arriver à ces détails, il est important de noter qu'un service Web XML n'a pas besoin d'être spécifiquement écrit pour gérer les demandes asynchrones à appeler de manière asynchrone. La classe proxy que vous créez pour votre client avec l'outil Web Services Description Language Tool (Wsdl.exe) crée automatiquement les méthodes permettant d'appeler la méthode de service Web XML de manière asynchrone. Ceci est vrai même s'il n'y a qu'une implémentation synchrone de la méthode de service Web XML.
Modèle de design de l'appel de méthode asynchrone .NET Framework
Le modèle de design pour appeler les méthodes de manière asynchrone, spécifié par le .NET Framework, implique qu'il y ait deux méthodes asynchrones pour chaque méthode synchrone. Pour chaque méthode synchrone, il y a les méthodes asynchrones Begin et End. La méthode Begin est appelée par un client pour démarrer ou commencer l'appel de méthode. C'est-à-dire que le client ordonne à la méthode de commencer à traiter l'appel de méthode mais le retour est immédiat. La méthode End est appelée par le client pour obtenir les résultats du traitement effectué par l'appel de méthode de service Web XML.
Comment un client sait-il quand il doit appeler la méthode End ? Pour répondre à cette question, il existe deux méthodes d'implémentation d'un client qui sont définies par le .NET Framework. La première consiste à passer une fonction de rappel dans la méthode Begin qui est alors appelée lorsque le traitement de la méthode se termine. La seconde utilise l'une des méthodes de la classe WaitHandle pour déclencher la mise en attente du client jusqu'à ce que la méthode soit terminée. Lorsqu'un client implémente la seconde méthode et appelle la méthode Begin, la valeur de retour n'est pas le type de données spécifié par la méthode de service Web XML mais plutôt un type implémentant l'interface IAsyncResult. L'interface IAsyncResult contient une propriété AsyncWaitHandle de type **WaitHandle **; cette propriété implémente les méthodes prenant en charge l'attente du signalement des objets de synchronisation à l'aide de WaitHandle.WaitOne, WaitAny et WaitAll. Lorsqu'un objet de synchronisation est signalé, cela indique que les threads attendant la ressource spécifiée peuvent alors y accéder. Si un client de service Web XML utilise la méthode Wait pour appeler de manière asynchrone une seule méthode de service Web XML, il peut appeler WaitOne pour attendre que le traitement de cette méthode se termine.
Il est important de noter qu'indépendamment de la méthode qu'un client choisit pour communiquer avec un service Web XML de manière asynchrone, les messages SOAP transmis et reçus sont identiques à ceux de la communication synchrone. C'est-à-dire qu'il n'y a toujours qu'une seule demande et réponse SOAP transmise et reçue sur le réseau. La classe proxy y parvient en gérant la réponse SOAP avec un thread différent de celui que le client a utilisé pour appeler la méthode Begin. Par conséquent, le client peut continuer à effectuer une autre tâche sur son thread pendant que la classe proxy gère la réception et le traitement de la réponse SOAP.
Implémentation d'un client de service Web XML qui appelle une méthode asynchrone
L'infrastructure permettant d'appeler un service Web XML de manière asynchrone à partir d'un client de service Web XML créé à l'aide d'ASP.NET est intégrée dans le .NET Framework et la classe proxy créée par l'outil Web Services Description Language Tool (Wsdl.exe). Le modèle de design pour l'appel asynchrone est défini par le .NET Framework, la classe proxy fournit le mécanisme pour communiquer avec une méthode de service Web XML de manière asynchrone. Lorsqu'une classe proxy est créée pour un service Web XML à l'aide de Wsdl.exe, trois méthodes sont créées pour chaque méthode publique dans le service Web XML. Le tableau suivant décrit ces trois méthodes.
Nom de la méthode dans la classe proxy | Description |
---|---|
<NameOfWebServiceMethod> | Envoie un message pour la méthode de service Web XML nommée <NameOfWebServiceMethod> de manière synchrone. |
Begin<NameOfWebServiceMethod> | Commence une communication par message asynchrone avec une méthode de service Web XML nommée <NameOfWebServiceMethod>. |
End<NameOfWebServiceMethod> | Met fin à une communication par message asynchrone avec une méthode de service Web XML nommée <NameOfWebServiceMethod> et reçoit le message terminé de la méthode. |
L'exemple de code suivant est une méthode de service Web XML qui peut nécessiter un moment relativement long pour terminer le traitement. C'est donc un bon exemple de définition du moment où votre client de service Web XML doit appeler la méthode du service de manière asynchrone.
<%@ WebService Language="C#" Class="PrimeFactorizer" %>
using System;
using System.Collections;
using System.Web.Services;
class PrimeFactorizer {
[WebMethod]
public long[] Factorize(long factorizableNum){
ArrayList outList = new ArrayList();
long i = 0;
int j;
try{
long Check = factorizableNum;
//Go through every possible integer
//factor between 2 and factorizableNum / 2.
//Thus, for 21, check between 2 and 10.
for (i = 2; i < (factorizableNum / 2); i++){
while(Check % i == 0){
outList.Add(i);
Check = (Check/i);
}
}
//Double-check to see how many prime factors have been added.
//If none, add 1 and the number.
j = outList.Count;
if (j == 0) {
outList.Add(1);
outList.Add(factorizableNum);
}
j = outList.Count;
//Return the results and
//create an array to hold them.
long[] primeFactor = new long[j];
for (j = 0; j < outList.Count; j++){
//Pass the values one by one, making sure
//to convert them to type ulong.
primeFactor[j] = Convert.ToInt64(outList[j]);
}
return primeFactor;
}
catch (Exception) {
return null;
}
}
}
[Visual Basic]
<%@ WebService Class="PrimeFactorizer" Language="VB" %>
Imports System
Imports System.Collections
Imports System.Web.Services
Public Class PrimeFactorizer
<WebMethod> _
Public Function Factorize(factorizableNum As Long) As Long()
Dim outList As New ArrayList()
Dim i As Long = 0
Dim j As Integer
Try
Dim Check As Long = factorizableNum
'Go through every possible integer
'factor between 2 and factorizableNum / 2.
'Thus, for 21, check between 2 and 10.
For i = 2 To CLng(factorizableNum / 2) - 1
While Check Mod i = 0
outList.Add(i)
Check = CLng(Check / i)
End While
Next i
'Double-check to see how many prime factors have been added.
'If none, add 1 and the number.
j = outList.Count
If j = 0 Then
outList.Add(1)
outList.Add(factorizableNum)
End If
j = outList.Count
'Return the results and
'create an array to hold them.
Dim primeFactor(j - 1) As Long
For j = 0 To outList.Count - 1
'Pass the values one by one, making sure
'to convert them to type ulong.
primeFactor(j) = CLng(outList(j))
Next j
Return primeFactor
Catch
Return Nothing
End Try
End Function
End Class
L'exemple de code suivant est une partie d'une classe proxy générée par Wsdl.exe pour la méthode de service Web XML précédente. Notez que les méthodes BeginFactorize
et EndFactorize
sont utilisées pour communiquer avec la méthode de service Web XML Factorize
de manière asynchrone.
public class PrimeFactorizer : System.Web.Services.Protocols.SoapHttpClientProtocol {
public long[] Factorize(long factorizableNum) {
object[] results = this.Invoke("Factorize", new object[] {
factorizableNum});
return ((long[])(results[0]));
}
public System.IAsyncResult BeginFactorize(long factorizableNum, System.AsyncCallback callback, object asyncState) {
return this.BeginInvoke("Factorize", new object[] {
factorizableNum}, callback, asyncState);
}
public long[] EndFactorize(System.IAsyncResult asyncResult) {
object[] results = this.EndInvoke(asyncResult);
return ((long[])(results[0]));
}
}
Il existe deux méthodes pour communiquer de manière asynchrone avec une méthode de service Web XML. L'exemple de code suivant illustre la communication asynchrone avec une méthode de service Web XML et l'utilisation d'une fonction de rappel pour extraire les résultats de la méthode.
using System;
using System.Runtime.Remoting.Messaging;
using MyFactorize;
class TestCallback
{
public static void Main(){
long factorizableNum = 12345;
PrimeFactorizer pf = new PrimeFactorizer();
//Instantiate an AsyncCallback delegate to use as a parameter
//in the BeginFactorize method.
AsyncCallback cb = new AsyncCallback(TestCallback.FactorizeCallback);
// Begin the Async call to Factorize, passing in our
// AsyncCalback delegate and a reference
// to our instance of PrimeFactorizer.
IAsyncResult ar = pf.BeginFactorize(factorizableNum, cb, pf);
// Keep track of the time it takes to complete the async call
// as the call proceeds.
int start = DateTime.Now.Second;
int currentSecond = start;
while (ar.IsCompleted == false){
if (currentSecond < DateTime.Now.Second) {
currentSecond = DateTime.Now.Second;
Console.WriteLine("Seconds Elapsed..." + (currentSecond - start).ToString() );
}
}
// Once the call has completed, you need a method to ensure the
// thread executing this Main function
// doesn't complete prior to the call-back function completing.
Console.Write("Press Enter to quit");
int quitchar = Console.Read();
}
// Set up a call-back function that is invoked by the proxy class
// when the asynchronous operation completes.
public static void FactorizeCallback(IAsyncResult ar)
{
// You passed in our instance of PrimeFactorizer in the third
// parameter to BeginFactorize, which is accessible in the
// AsyncState property.
PrimeFactorizer pf = (PrimeFactorizer) ar.AsyncState;
long[] results;
// Get the completed results.
results = pf.EndFactorize(ar);
//Output the results.
Console.Write("12345 factors into: ");
int j;
for (j = 0; j<results.Length;j++){
if (j == results.Length - 1)
Console.WriteLine(results[j]);
else
Console.Write(results[j] + ", ");
}
}
}
[Visual Basic]
Imports System
Imports System.Runtime.Remoting.Messaging
Imports MyFactorize
Public Class TestCallback
Public Shared Sub Main()
Dim factorizableNum As Long = 12345
Dim pf As PrimeFactorizer = new PrimeFactorizer()
'Instantiate an AsyncCallback delegate to use as a parameter
' in the BeginFactorize method.
Dim cb as AsyncCallback
cb = new AsyncCallback(AddressOf TestCallback.FactorizeCallback)
' Begin the Async call to Factorize, passing in the
' AsyncCallback delegate and a reference to our instance
' of PrimeFactorizer.
Dim ar As IAsyncResult = pf.BeginFactorize(factorizableNum, _
cb, pf)
' Keep track of the time it takes to complete the async call as
' the call proceeds.
Dim start As Integer = DateTime.Now.Second
Dim currentSecond As Integer = start
Do while (ar.IsCompleted = false)
If (currentSecond < DateTime.Now.Second) Then
currentSecond = DateTime.Now.Second
Console.WriteLine("Seconds Elapsed..." + (currentSecond - start).ToString() )
End If
Loop
' Once the call has completed, you need a method to ensure the
' thread executing this Main function
' doesn't complete prior to the callback function completing.
Console.Write("Press Enter to quit")
Dim quitchar As Integer = Console.Read()
End Sub
' Set up the call-back function that is invoked by the proxy
' class when the asynchronous operation completes.
Public Shared Sub FactorizeCallback(ar As IAsyncResult)
' You passed in the instance of PrimeFactorizer in the third
' parameter to BeginFactorize, which is accessible in the
' AsyncState property.
Dim pf As PrimeFactorizer = ar.AsyncState
Dim results() as Long
' Get the completed results.
results = pf.EndFactorize(ar)
'Output the results.
Console.Write("12345 factors into: ")
Dim j as Integer
For j = 0 To results.Length - 1
If j = (results.Length - 1) Then
Console.WriteLine(results(j) )
Else
Console.Write(results(j).ToString + ", ")
End If
Next j
End Sub
End Class
L'exemple de code suivant illustre la communication asynchrone avec une méthode de service Web XML puis l'utilisation d'un objet de synchronisation pour attendre la fin du traitement.
// -----------------------------------------------------------------------// Async Variation 2.
// Asynchronously invoke the Factorize method,
//without specifying a call back.
using System;
using System.Runtime.Remoting.Messaging;
// MyFactorize, is the name of the namespace in which the proxy class is
// a member of for this sample.
using MyFactorize;
class TestCallback
{
public static void Main(){
long factorizableNum = 12345;
PrimeFactorizer pf = new PrimeFactorizer();
// Begin the Async call to Factorize.
IAsyncResult ar = pf.BeginFactorize(factorizableNum, null, null);
// Wait for the asynchronous operation to complete.
ar.AsyncWaitHandle.WaitOne();
// Get the completed results.
long[] results;
results = pf.EndFactorize(ar);
//Output the results.
Console.Write("12345 factors into: ");
int j;
for (j = 0; j<results.Length;j++){
if (j == results.Length - 1)
Console.WriteLine(results[j]);
else
Console.Write(results[j] + ", ");
}
}
}
[Visual Basic]
Imports System
Imports System.Runtime.Remoting.Messaging
Imports MyFactorize ' Proxy class namespace
Public Class TestCallback
Public Shared Sub Main()
Dim factorizableNum As Long = 12345
Dim pf As PrimeFactorizer = new PrimeFactorizer()
' Begin the Async call to Factorize.
Dim ar As IAsyncResult = pf.BeginFactorize(factorizableNum, Nothing, Nothing)
' Wait for the asynchronous operation to complete.
ar.AsyncWaitHandle.WaitOne()
' Get the completed results.
Dim results() as Long
results = pf.EndFactorize(ar)
'Output the results.
Console.Write("12345 factors into: ")
Dim j as Integer
For j = 0 To results.Length - 1
If j = (results.Length - 1) Then
Console.WriteLine(results(j) )
Else
Console.Write(results(j).ToString + ", ")
End If
Next j
End Sub
End Class
Notez que si FactorizeCallback
est une classe liée au contexte qui nécessite un contexte synchronisé/ayant des points communs avec les threads, le rappel est distribué dans l'infrastructure de répartition du contexte. En d'autres termes, dans de tels contextes, le rappel peut s'exécuter de manière asynchrone par rapport à son appelant. Il s'agit précisément de la sémantique d'un qualificateur unilatéral sur les signatures de méthode. Cela signifie que n'importe quel appel de méthode peut s'exécuter de manière synchrone ou asynchrone par rapport à l'appelant, et celui-ci ne peut faire aucune supposition sur l'achèvement d'un tel appel lorsque le contrôle d'exécution lui est retourné.
L'appel à EndInvoke avant l'achèvement de l'opération asynchrone bloquera également l'appelant. Le comportement concernant un second appel à cette méthode avec le même AsyncResult n'est pas défini.
La méthode Cancel constitue une demande d'annulation du traitement de la méthode après que le délai spécifié a expiré. Notez qu'il s'agit d'une demande du client et que le serveur devrait y répondre. Le client ne doit pas nécessairement faire des suppositions quant à l'arrêt du traitement de la méthode par le serveur après avoir reçu le message d'annulation de la méthode. Il est recommandé que le client ne supprime pas des ressources telles que des fichiers objets car le serveur peut encore les utiliser. true sera affecté à la propriété IsCompleted de l'instance IAsyncResult après que le serveur a fini son traitement et n'utilisera plus les ressources fournies par le client. Ainsi, le client peut détruire en toute confiance les ressources après que la propriété IsCompleted a pris la valeur true.
Voir aussi
Création de clients de service Web XML | Découverte des services Web XML | Création de clients pour les services Web XML | Exploration des services Web XML existants créés à l'aide d'ASP.NET | Accès à des services Web XML à partir d'un navigateur