Cómo llamar a operaciones del servicio WCF de forma asincrónica

En este artículo se explica cómo puede acceder un cliente a una operación de servicio de forma asincrónica. El servicio de este artículo implementa la interfaz ICalculator. El cliente puede llamar a las operaciones de esta interfaz de forma asincrónica mediante el modelo de llamada asincrónica orientado a eventos. (Para obtener más información sobre el modelo de llamada asincrónica basado en eventos, vea Programación multiproceso con el patrón asincrónico basado en eventos). Para obtener un ejemplo que muestre cómo llamar a una operación de manera asincrónica en un servicio, consulte Procedimiento para implementar una operación de servicios asincrónica. Para obtener más información sobre las operaciones sincrónicas y asincrónicas, consulte Operaciones sincrónicas y asincrónicas.

Nota

El modelo de llamada asincrónica orientado a eventos no es compatible con la utilización de un ChannelFactory<TChannel>. Para obtener información sobre cómo realizar llamadas asincrónicas mediante ChannelFactory<TChannel>, consulte Procedimiento para llamar a operaciones de manera asincrónica mediante un generador de canales.

Procedimiento

Para llamar a operaciones de servicio WCF de forma asincrónica

  1. Ejecute la Herramienta de utilidad de metadatos de ServiceModel (Svcutil.exe) con las opciones de comando /async y /tcv:Version35 a la vez, como muestra el comando siguiente.

    svcutil /n:http://Microsoft.ServiceModel.Samples,Microsoft.ServiceModel.Samples http://localhost:8000/servicemodelsamples/service/mex /a /tcv:Version35  
    

    De este modo se genera, además de las operaciones sincrónicas y las operaciones asincrónicas estándar basadas en delegado, una clase de cliente WCF que contiene:

    • Dos operaciones <operationName>Async que se utilizan con el enfoque de llamada asincrónica basada en eventos. Por ejemplo:

      public void AddAsync(double n1, double n2)
      {
          this.AddAsync(n1, n2, null);
      }
      
      public void AddAsync(double n1, double n2, object userState)
      {
          if ((this.onBeginAddDelegate == null))
          {
              this.onBeginAddDelegate = new BeginOperationDelegate(this.OnBeginAdd);
          }
          if ((this.onEndAddDelegate == null))
          {
              this.onEndAddDelegate = new EndOperationDelegate(this.OnEndAdd);
          }
          if ((this.onAddCompletedDelegate == null))
          {
              this.onAddCompletedDelegate = new System.Threading.SendOrPostCallback(this.OnAddCompleted);
          }
          base.InvokeAsync(this.onBeginAddDelegate, new object[] {
                      n1,
                      n2}, this.onEndAddDelegate, this.onAddCompletedDelegate, userState);
      }
      
      Public Overloads Sub AddAsync(ByVal n1 As Double, ByVal n2 As Double)
          Me.AddAsync(n1, n2, Nothing)
      End Sub
      
      Public Overloads Sub AddAsync(ByVal n1 As Double, ByVal n2 As Double, ByVal userState As Object)
          If (Me.onBeginAddDelegate Is Nothing) Then
              Me.onBeginAddDelegate = AddressOf Me.OnBeginAdd
          End If
          If (Me.onEndAddDelegate Is Nothing) Then
              Me.onEndAddDelegate = AddressOf Me.OnEndAdd
          End If
          If (Me.onAddCompletedDelegate Is Nothing) Then
              Me.onAddCompletedDelegate = AddressOf Me.OnAddCompleted
          End If
          MyBase.InvokeAsync(Me.onBeginAddDelegate, New Object() {n1, n2}, Me.onEndAddDelegate, Me.onAddCompletedDelegate, userState)
      End Sub
      
    • Eventos completados de operación del formulario <operationName>Completed que se utilizan con el enfoque de llamada asincrónica basada en eventos. Por ejemplo:

      public event System.EventHandler<AddCompletedEventArgs> AddCompleted;
      
      Public Event AddCompleted As System.EventHandler(Of AddCompletedEventArgs)
      
    • Tipos System.EventArgs para cada operación (del formulario <operationName>CompletedEventArgs) que se utilizan en el enfoque de llamada asincrónica basada en eventos. Por ejemplo:

      [System.Diagnostics.DebuggerStepThroughAttribute()]
      [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
      public partial class AddCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
      {
          private object[] results;
      
          public AddCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) :
                  base(exception, cancelled, userState)
          {       this.results = results;         }
      
          public double Result
          {
              get            {
                  base.RaiseExceptionIfNecessary();
                  return ((double)(this.results[0]));
              }
          }
      }
      
      <System.Diagnostics.DebuggerStepThroughAttribute(),  _
       System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")>  _
      Partial Public Class AddCompletedEventArgs
          Inherits System.ComponentModel.AsyncCompletedEventArgs
          
          Private results() As Object
          
          Public Sub New(ByVal results() As Object, ByVal exception As System.Exception, ByVal cancelled As Boolean, ByVal userState As Object)
              MyBase.New(exception, cancelled, userState)
              Me.results = results
          End Sub
          
          Public ReadOnly Property Result() As Double
              Get
                  MyBase.RaiseExceptionIfNecessary
                  Return CType(Me.results(0),Double)
              End Get
          End Property
      End Class
      
  2. En la aplicación de llamada, cree un método de devolución de llamada al que llamar cuando la operación asincrónica finalice, tal y como se muestra en el siguiente código de ejemplo.

    // Asynchronous callbacks for displaying results.
    static void AddCallback(object sender, AddCompletedEventArgs e)
    {
        Console.WriteLine("Add Result: {0}", e.Result);
    }
    
    ' Asynchronous callbacks for displaying results.
    Private Shared Sub AddCallback(ByVal sender As Object, ByVal e As AddCompletedEventArgs)
    
        Console.WriteLine("Add Result: {0}", e.Result)
    
    End Sub
    
  3. Antes de llamar a la operación, use un nuevo elemento System.EventHandler<TEventArgs> genérico de tipo <operationName>EventArgs para agregar el método de control (creado en el paso anterior) al evento <operationName>Completed. A continuación, llame al método <operationName>Async. Por ejemplo:

    // AddAsync
    double value1 = 100.00D;
    double value2 = 15.99D;
    client.AddCompleted += new EventHandler<AddCompletedEventArgs>(AddCallback);
    client.AddAsync(value1, value2);
    Console.WriteLine("Add({0},{1})", value1, value2);
    
    ' AddAsync
    Dim value1 As Double = 100
    Dim value2 As Double = 15.99
    AddHandler client.AddCompleted, AddressOf AddCallback
    client.AddAsync(value1, value2)
    Console.WriteLine("Add({0},{1})", value1, value2)
    

Ejemplo

Nota

Las directrices de diseño del modelo asincrónico basado en eventos afirman que si se devuelve más de un valor, uno de los valores se devuelve como propiedad Result y los demás se devuelven como propiedades del objeto EventArgs. Una de las consecuencias de esto, es que el cliente importa metadatos utilizando las opciones de comando asincrónicas basadas en eventos y la operación devuelve más de una valor, el objeto predeterminado EventArgs devuelve un valor como propiedad Result, y el resto son propiedades del objeto EventArgs.Para recibir el objeto de mensaje como propiedad Result y que los valores devueltos sean propiedades de ese objeto, se utiliza la opción de comando /messageContract. Esto genera una firma que devuelve el mensaje de respuesta como la propiedad Result del objeto EventArgs. Todos los valores de devolución internos se convierten, pues, en propiedades del objeto de mensaje de respuesta.

using System;

namespace Microsoft.ServiceModel.Samples
{
    // The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool.

    class Client
    {
        static void Main()
        {
            Console.WriteLine("Press <ENTER> to terminate client once the output is displayed.");
            Console.WriteLine();

            // Create a client
            CalculatorClient client = new CalculatorClient();

            // AddAsync
            double value1 = 100.00D;
            double value2 = 15.99D;
            client.AddCompleted += new EventHandler<AddCompletedEventArgs>(AddCallback);
            client.AddAsync(value1, value2);
            Console.WriteLine("Add({0},{1})", value1, value2);

            // SubtractAsync
            value1 = 145.00D;
            value2 = 76.54D;
            client.SubtractCompleted += new EventHandler<SubtractCompletedEventArgs>(SubtractCallback);
            client.SubtractAsync(value1, value2);
            Console.WriteLine("Subtract({0},{1})", value1, value2);

            // Multiply
            value1 = 9.00D;
            value2 = 81.25D;
            double result = client.Multiply(value1, value2);
            Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);

            // Divide
            value1 = 22.00D;
            value2 = 7.00D;
            result = client.Divide(value1, value2);
            Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);

            Console.ReadLine();

            //Closing the client gracefully closes the connection and cleans up resources
            client.Close();
        }

        // Asynchronous callbacks for displaying results.
        static void AddCallback(object sender, AddCompletedEventArgs e)
        {
            Console.WriteLine("Add Result: {0}", e.Result);
        }

        static void SubtractCallback(object sender, SubtractCompletedEventArgs e)
        {
            Console.WriteLine("Subtract Result: {0}", e.Result);
        }
    }
}
Namespace Microsoft.ServiceModel.Samples

    ' The service contract is defined in generatedClient.vb, generated from the service by the svcutil tool.

    Class Client

        Public Shared Sub Main()

            Console.WriteLine("Press <ENTER> to terminate client once the output is displayed.")
            Console.WriteLine()

            ' Create a client
            Dim client As New CalculatorClient()

            ' AddAsync
            Dim value1 As Double = 100
            Dim value2 As Double = 15.99
            AddHandler client.AddCompleted, AddressOf AddCallback
            client.AddAsync(value1, value2)
            Console.WriteLine("Add({0},{1})", value1, value2)

            ' SubtractAsync
            value1 = 145
            value2 = 76.54
            AddHandler client.SubtractCompleted, AddressOf SubtractCallback
            client.SubtractAsync(value1, value2)
            Console.WriteLine("Subtract({0},{1})", value1, value2)

            ' Multiply
            value1 = 9
            value2 = 81.25
            Dim result As Double = client.Multiply(value1, value2)
            Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result)

            ' Divide
            value1 = 22
            value2 = 7
            result = client.Divide(value1, value2)
            Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result)
            Console.ReadLine()

            'Closing the client gracefully closes the connection and cleans up resources
            client.Close()

        End Sub
        ' Asynchronous callbacks for displaying results.
        Private Shared Sub AddCallback(ByVal sender As Object, ByVal e As AddCompletedEventArgs)

            Console.WriteLine("Add Result: {0}", e.Result)

        End Sub
        Private Shared Sub SubtractCallback(ByVal sender As Object, ByVal e As SubtractCompletedEventArgs)

            Console.WriteLine("Subtract Result: {0}", e.Result)

        End Sub

    End Class

End Namespace

Consulte también