Comunicazione asincrona con servizi Web XML

La comunicazione asincrona con un servizio Web XML segue il modello di progettazione asincrono utilizzato in Microsoft .NET Framework. Prima di entrare nei dettagli, è tuttavia importante notare che per essere chiamato in modo asincrono, un servizio Web XML non deve necessariamente essere stato sviluppato appositamente per la gestione di richieste asincrone. La classe proxy creata per il client con lo Strumento del linguaggio di descrizione dei servizi Web (Wsdl.exe) crea automaticamente i metodi per chiamare il metodo di servizio Web XML in modo asincrono. L'impostazione è valida anche se il metodo di servizio Web XML presenta solo un'implementazione sincrona.

Modello di progettazione per chiamate di metodi asincrone di .NET Framework

Il modello di progettazione per chiamare i metodi in modo asincrono specificato da .NET Framework si basa sull'assunto secondo cui sono disponibili due metodi asincroni per ciascun metodo sincrono. Per ogni metodo sincrono, sono disponibili un metodo asincrono Begin e un metodo asincrono End. Il metodo Begin viene chiamato da un client per avviare la chiamata di metodo. Il client comunica al metodo di iniziare l'elaborazione della chiamata di metodo ma esegue immediatamente la restituzione. Il metodo End, invece, viene chiamato dal client per ottenere i risultati dell'elaborazione eseguita dalla chiamata del metodo di servizio Web XML.

Per sapere quando chiamare il metodo End, sono disponibili due metodi, come definito in .NET Framework. Il primo consiste nel passare una funzione di callback al metodo Begin, che viene chiamato al termine dell'elaborazione del metodo. Il secondo consiste nell'utilizzare uno dei metodi della classe WaitHandle per porre il client in attesa fino al completamento del metodo. Quando un client implementa il secondo metodo e chiama il metodo Begin, il valore restituito non è il tipo di dati specificato dal metodo di servizio Web XML, bensì il tipo che implementa l'interfaccia IAsyncResult. L'interfaccia IAsyncResult contiene una proprietà AsyncWaitHandle di tipo WaitHandle, che implementa metodi che consentono di attendere che gli oggetti di sincronizzazione vengano segnalati con WaitHandle.WaitOne, WaitAny e WaitAll. La segnalazione di un oggetto di sincronizzazione indica che i thread in attesa della risorsa specificata possono accedere alla risorsa. Se il client di un servizio Web XML utilizza il metodo di attesa per la chiamata asincrona di un solo metodo di servizio Web XML, potrà chiamare WaitOne per attendere il completamento dell'elaborazione di tale metodo.

Si noti che indipendentemente dal metodo scelto per la comunicazione asincrona con un servizio Web XML, i messaggi SOAP inviati e ricevuti sono identici alla comunicazione in modo sincrono. Ovvero, solo una richiesta SOAP viene inviata e solo una risposta SOAP viene ricevuta tramite la rete. La classe proxy esegue questo processo gestendo la risposta SOAP tramite un thread diverso rispetto a quello utilizzato dal client per chiamare il metodo Begin. Pertanto, il client può continuare ad eseguire altre operazioni sul proprio thread, mentre la classe proxy gestisce la ricezione e l'elaborazione della risposta SOAP.

Implementazione di un client del servizio Web XML che esegue chiamate di metodi asincrone

L'infrastruttura che consente di eseguire chiamate asincrone da un client del servizio Web XML creato con ASP.NET a un servizio Web XML è incorporata in .NET Framework e nella classe proxy creata dallo Strumento del linguaggio di descrizione dei servizi Web (Wsdl.exe). Il modello di progettazione per le chiamate asincrone è definito da .NET Framework e la classe proxy fornisce il meccanismo per la comunicazione asincrona con un metodo di servizio Web XML. Quando viene creata una classe proxy per un servizio Web XML utilizzando Wsdl.exe, vengono creati tre metodi per ogni metodo di servizio Web XML pubblico nel servizio Web XML. Nella tabella seguente vengono descritti questi tre metodi.

Nome del metodo nella classe proxy Descrizione
<NameOfWebServiceMethod> Invia un messaggio in modo sincrono per il metodo di servizio Web XML denominato <NameOfWebServiceMethod>.
Begin<NameOfWebServiceMethod> Inizia la comunicazione asincrona di un messaggio con un metodo di servizio Web XML denominato <NameOfWebServiceMethod>.
End<NameOfWebServiceMethod> Termina la comunicazione asincrona di un messaggio con un metodo di servizio Web XML denominato <NameOfWebServiceMethod>, recuperando il messaggio completo dal metodo di servizio Web XML.

Nell'esempio di codice seguente viene mostrato un metodo di servizio Web XML che potrebbe richiedere un periodo di tempo relativamente lungo per terminare il processo di elaborazione. Rappresenta quindi un buon esempio di un caso in cui è opportuno impostare il client del servizio Web XML per chiamare il metodo di servizio Web XML in modo asincrono.

<%@ 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

Nell'esempio di codice seguente è riportata parte di una classe proxy generata da Wsdl.exe per il metodo di servizio Web XML illustrato in precedenza. Si notino i metodi BeginFactorize e EndFactorize, utilizzati per comunicare con il metodo di servizio Web XML Factorize in modo asincrono.

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

Per la comunicazione asincrona con un metodo di servizio Web XML sono disponibili due metodi. Nell'esempio di codice che segue vengono mostrati una comunicazione asincrona con un metodo di un servizio Web XML e l'utilizzo di una funzione di callback per il recupero dei risultati forniti dal metodo del servizio Web XML.

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

Nell'esempio di codice seguente viene descritta la comunicazione asincrona con un metodo di servizio Web XML, quindi l'utilizzo di un oggetto di sincronizzazione per attendere la fine dell'elaborazione.

// -----------------------------------------------------------------------// 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

Si noti che se FactorizeCallback è una classe associata a un contesto che richiede un contesto sincronizzato/di affinità del thread, il callback verrà inviato tramite l'infrastruttura del dispatcher del contesto. In altri termini, per questi contesti il callback può essere eseguito in modo asincrono rispetto al relativo chiamante. Si tratta per la precisione della semantica del qualificatore unidirezionale sulle firme del metodo. Qualsiasi chiamata al metodo può essere quindi eseguita in modo sincrono o asincrono rispetto al chiamante, il quale non può effettuare alcun presupposto sul completamento di tale chiamata quando il controllo dell'esecuzione viene restituito.

Inoltre, la chiamata di EndInvoke prima del completamento dell'operazione asincrona bloccherà il chiamante. La funzionalità che consente di eseguire la chiamata per la seconda volta con lo stesso AsyncResult non è definita.

Il metodo Cancel è una richiesta di annullamento dell'elaborazione del metodo al termine del periodo di timeout specificato. Si noti che è una richiesta del client ed è consigliabile che venga accettata dal server. Non è necessario che il client effettui presupposti sulla possibilità che il server abbia interrotto l'elaborazione del metodo dopo aver ricevuto il messaggio che il metodo è stato annullato. È consigliabile che il client non elimini le risorse, ad esempio oggetti file, poiché potrebbero essere ancora utilizzate dal server. La proprietà IsCompleted dell'istanza di IAsyncResult verrà impostata su true dopo che il server avrà terminato l'elaborazione e non utilizzerà più alcuna risorsa fornita dal client. Il client può quindi eliminare le risorse in tutta sicurezza dopo che la proprietà IsCompleted è stata impostata su true.

Vedere anche

Generazione di client dei servizi Web XML | Individuazione di servizi Web XML | Creazione di client per servizi Web XML | Accesso a servizi Web XML esistenti creati con ASP.NET | Accesso ai servizi Web XML da un browser