Compartir a través de


Aplicaciones ASP.NET mediante controladores de espera (ADO.NET)

Los modelos de devolución de llamada y sondeo para el control de operaciones asincrónicas resultan de utilidad cuando la aplicación procesa una sola operación asincrónica cada vez. Los modelos Wait ofrecen una manera más flexible de procesar varias operaciones asincrónicas. Existen dos modelos Wait, cuyo nombre se debe a los métodos WaitHandle usados para implementarlos: el modelo Wait (Any) y el modelo Wait (All).

Para utilizar cualquiera de ellos, debe emplear la propiedad AsyncWaitHandle del objeto IAsyncResult que devuelven los métodos BeginExecuteNonQuery, BeginExecuteReader o BeginExecuteXmlReader. Tanto el método WaitAny como el método WaitAll requieren que envíe los objetos WaitHandle como argumentos, agrupados juntos en una matriz.

Ambos métodos Wait supervisan las operaciones asincrónicas y esperan a que se completen. El método WaitAny espera a que cualquiera de las operaciones se complete o agote su tiempo de espera. Una vez que conoce que una determinada operación se ha completado, puede procesar sus resultados y luego seguir a la espera de que la siguiente operación se complete o agote su tiempo de espera. El método WaitAll espera a que todos los procesos de la matriz de instancias WaitHandle se completen o agoten su tiempo de espera antes de continuar.

Los beneficios de los modelos Wait son más notables cuando necesita ejecutar varias operaciones de cierta longitud en servidores diferentes, o cuando el servidor tiene la suficiente potencia como para procesar todas las consultas al mismo tiempo. En los ejemplos aquí presentados, tres consultas emulan procesos largos con la adición de comandos WAITFOR de diversas longitudes a consultas SELECT ilógicas.

Ejemplo: Modelo Wait (Any)

El siguiente ejemplo ilustra el modelo Wait (Any). Una vez iniciados tres procesos asincrónicos, se llama al método WaitAny para esperar a que se complete cualquiera de ellos. A medida que se completan los procesos, se llama al método EndExecuteReader y se lee el objeto SqlDataReader resultante. En este punto, a una aplicación del mundo real le gustaría utilizar el SqlDataReader para rellenar una parte de la página. En este sencillo ejemplo, la hora en que se completa el proceso se agrega al cuadro de texto correspondiente al proceso. Tomadas juntas, las horas de los cuadros de texto ilustran el punto: el código se ejecuta cada vez que se completa un proceso.

Para realizar este ejemplo, cree un nuevo proyecto de sitio web de ASP.NET. Coloque un control Button y cuatro controles TextBox en la página, y acepte el nombre predeterminado de cada control.

Agregue el siguiente código a la clase del formulario, modificando la cadena de conexión según sea necesario para su entorno.

[Visual Basic]

' Add these to the top of the class
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Threading

' Add this code to the page's class:
    Private Function GetConnectionString() As String
        ' To avoid storing the connection string in your code,            
        ' you can retrieve it from a configuration file. 

        ' If you have not included "Asynchronous Processing=true" 
        ' in the connection string, the command will not be able
        ' to execute asynchronously.
        Return "Data Source=(local);Integrated Security=SSPI;" & _
          "Initial Catalog=AdventureWorks;" & _
          "Asynchronous Processing=true"
    End Function  

    Sub Button1_Click( _
     ByVal sender As Object, ByVal e As System.EventArgs)

        ' In a real-world application, you might be connecting to 
        '  three different servers or databases. For the example,
        '  we connect to only one.
        Dim connection1 As New SqlConnection(GetConnectionString())
        Dim connection2 As New SqlConnection(GetConnectionString())
        Dim connection3 As New SqlConnection(GetConnectionString())

        ' To keep the example simple, all three asynchronous 
        ' processes select a row from the same table. WAITFOR
        ' commands are used to emulate long-running processes
        ' that complete after different periods of time.
        Dim commandText1 As String = _
            "WAITFOR DELAY '0:0:01';" & _
            "SELECT * FROM Production.Product " & _
            "WHERE ProductNumber = 'BL-2036'"

        Dim commandText2 As String = _
            "WAITFOR DELAY '0:0:05';" & _
            "SELECT * FROM Production.Product " & _
            "WHERE ProductNumber = 'BL-2036'"

        Dim commandText3 As String = _
            "WAITFOR DELAY '0:0:10';" & _
            "SELECT * FROM Production.Product " & _
            "WHERE ProductNumber = 'BL-2036'"

        Dim waitHandles(2) As WaitHandle
        Try
            ' For each process, open a connection and begin execution.
            ' Use the IAsyncResult object returned by 
            ' BeginExecuteReader to add a WaitHandle for the process
            ' to the array.
            connection1.Open()
            Dim command1 As New SqlCommand(commandText1, connection1)
            Dim result1 As IAsyncResult = _
             command1.BeginExecuteReader()
            waitHandles(0) = result1.AsyncWaitHandle

            connection2.Open()
            Dim command2 As New SqlCommand(commandText2, connection2)
            Dim result2 As IAsyncResult = _
             command2.BeginExecuteReader()
            waitHandles(1) = result2.AsyncWaitHandle

            connection3.Open()
            Dim command3 As New SqlCommand(commandText3, connection3)
            Dim result3 As IAsyncResult = _
             command3.BeginExecuteReader()
            waitHandles(2) = result3.AsyncWaitHandle

            Dim index As Integer
            For countWaits As Integer = 1 To 3
                ' WaitAny waits for any of the processes to complete.
                ' The return value is either the index of the
                ' array element whose process just completed, or
                ' the WaitTimeout value.
                index = WaitHandle.WaitAny(waitHandles, 60000, False)
                ' This example doesn't actually do anything with the 
                ' data returned by the processes, but the code opens 
                ' readers for each just to demonstrate the concept.
                ' Instead of using the returned data to fill the 
                ' controls on the page, the example adds the time
                ' the process was completed to the corresponding
                ' text box.
                Select Case index
                    Case 0
                        Dim reader1 As SqlDataReader
                        reader1 = command1.EndExecuteReader(result1)
                        If reader1.Read Then
                            TextBox1.Text = _
                             "Completed " & _
                             System.DateTime.Now.ToLongTimeString()
                        End If
                        reader1.Close()

                    Case 1
                        Dim reader2 As SqlDataReader
                        reader2 = command2.EndExecuteReader(result2)
                        If reader2.Read Then
                            TextBox2.Text = _
                             "Completed " & _
                             System.DateTime.Now.ToLongTimeString()
                        End If
                        reader2.Close()
                    Case 2
                        Dim reader3 As SqlDataReader
                        reader3 = command3.EndExecuteReader(result3)
                        If reader3.Read Then
                            TextBox3.Text = _
                             "Completed " & _
                             System.DateTime.Now.ToLongTimeString()
                        End If
                        reader3.Close()
                    Case WaitHandle.WaitTimeout
                        Throw New Exception("Timeout")
                End Select

            Next
        Catch ex As Exception
            TextBox4.Text = ex.ToString
        End Try
        connection1.Close()
        connection2.Close()
        connection3.Close()

    End Sub

[C#]

// Add the following using statements, if they are not already there.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Threading;
using System.Data.SqlClient;

// Add this code to the page's class
string GetConnectionString()
     //  To avoid storing the connection string in your code,            
     //  you can retrieve it from a configuration file. 
     //  If you have not included "Asynchronous Processing=true" 
     //  in the connection string, the command will not be able
     //  to execute asynchronously.
{
     return "Data Source=(local);Integrated Security=SSPI;" +
          "Initial Catalog=AdventureWorks;" +
          "Asynchronous Processing=true";
}
void Button1_Click(object sender, System.EventArgs e)
{
     //  In a real-world application, you might be connecting to 
     //   three different servers or databases. For the example,
     //   we connect to only one.
     
     SqlConnection connection1 = 
          new SqlConnection(GetConnectionString());
     SqlConnection connection2 = 
          new SqlConnection(GetConnectionString());
     SqlConnection connection3 = 
          new SqlConnection(GetConnectionString());
     //  To keep the example simple, all three asynchronous 
     //  processes select a row from the same table. WAITFOR
     //  commands are used to emulate long-running processes
     //  that complete after different periods of time.

     string commandText1 = "WAITFOR DELAY '0:0:01';" + 
          "SELECT * FROM Production.Product " + 
          "WHERE ProductNumber = 'BL-2036'";
     string commandText2 = "WAITFOR DELAY '0:0:05';" + 
          "SELECT * FROM Production.Product " + 
          "WHERE ProductNumber = 'BL-2036'";
     string commandText3 = "WAITFOR DELAY '0:0:10';" + 
          "SELECT * FROM Production.Product " + 
          "WHERE ProductNumber = 'BL-2036'";
     try
          //  For each process, open a connection and begin 
          //  execution. Use the IAsyncResult object returned by 
          //  BeginExecuteReader to add a WaitHandle for the 
          //  process to the array.
     {
          connection1.Open();
          SqlCommand command1 =
               new SqlCommand(commandText1, connection1);
          IAsyncResult result1 = command1.BeginExecuteReader();
          WaitHandle waitHandle1 = result1.AsyncWaitHandle;

          connection2.Open();
          SqlCommand command2 =
               new SqlCommand(commandText2, connection2);
          IAsyncResult result2 = command2.BeginExecuteReader();
          WaitHandle waitHandle2 = result2.AsyncWaitHandle;

          connection3.Open();
          SqlCommand command3 =
               new SqlCommand(commandText3, connection3);
          IAsyncResult result3 = command3.BeginExecuteReader();
          WaitHandle waitHandle3 = result3.AsyncWaitHandle;

          WaitHandle[] waitHandles = {
               waitHandle1, waitHandle2, waitHandle3
          };

          int index;
          for (int countWaits = 0; countWaits <= 2; countWaits++)
          {
               //  WaitAny waits for any of the processes to 
               //  complete. The return value is either the index 
               //  of the array element whose process just 
               //  completed, or the WaitTimeout value.

               index = WaitHandle.WaitAny(waitHandles, 
                    60000, false);
               //  This example doesn't actually do anything with 
               //  the data returned by the processes, but the 
               //  code opens readers for each just to demonstrate     
               //  the concept.
               //  Instead of using the returned data to fill the 
               //  controls on the page, the example adds the time
               //  the process was completed to the corresponding
               //  text box.

               switch (index)
               {
                    case 0:
                         SqlDataReader reader1;
                         reader1 = 
                              command1.EndExecuteReader(result1);
                         if (reader1.Read())
                         {
                           TextBox1.Text = 
                           "Completed " +
                           System.DateTime.Now.ToLongTimeString();
                         }
                         reader1.Close();
                         break;
                    case 1:
                         SqlDataReader reader2;
                         reader2 = 
                              command2.EndExecuteReader(result2);
                         if (reader2.Read())
                         {
                           TextBox2.Text = 
                           "Completed " +
                           System.DateTime.Now.ToLongTimeString();
                         }
                         reader2.Close();
                         break;
                    case 2:
                         SqlDataReader reader3;
                         reader3 = 
                              command3.EndExecuteReader(result3);
                         if (reader3.Read())
                         {
                           TextBox3.Text = 
                           "Completed " +
                           System.DateTime.Now.ToLongTimeString();
                         }
                         reader3.Close();
                         break;
                    case WaitHandle.WaitTimeout:
                         throw new Exception("Timeout");
                         break;
               }
          }
     }
     catch (Exception ex)
     {
          TextBox4.Text = ex.ToString();
     }
     connection1.Close();
     connection2.Close();
     connection3.Close();
}

Ejemplo: Modelo Wait (All)

El siguiente ejemplo ilustra el modelo Wait (All). Una vez iniciados tres procesos asincrónicos, se llama al método WaitAll para esperar a que se completen los procesos o agoten su tiempo de espera.

Al igual que en el ejemplo del modelo Wait (Any), la hora en que se completa el proceso se agrega al cuadro de texto correspondiente al proceso. De nuevo, las horas de los cuadros de texto ilustran el punto: el código que sigue al método WaitAny solo se ejecuta una vez completados todos los procesos.

Para realizar este ejemplo, cree un nuevo proyecto de sitio web de ASP.NET. Coloque un control Button y cuatro controles TextBox en la página, y acepte el nombre predeterminado de cada control.

Agregue el siguiente código a la clase del formulario, modificando la cadena de conexión según sea necesario para su entorno.

[Visual Basic]

' Add these to the top of the class
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Threading

' Add this code to the page's class:
    Private Function GetConnectionString() As String
        ' To avoid storing the connection string in your code,            
        ' you can retrieve it from a configuration file. 

        ' If you have not included "Asynchronous Processing=true" 
        ' in the connection string, the command will not be able
        ' to execute asynchronously.
        Return "Data Source=(local);Integrated Security=SSPI;" & _
          "Initial Catalog=AdventureWorks;" & _
          "Asynchronous Processing=true"
    End Function  
    Sub Button1_Click( _
     ByVal sender As Object, ByVal e As System.EventArgs)

        ' In a real-world application, you might be connecting to 
        '  three different servers or databases. For the example,
        '  we connect to only one.
        Dim connection1 As New SqlConnection(GetConnectionString())
        Dim connection2 As New SqlConnection(GetConnectionString())
        Dim connection3 As New SqlConnection(GetConnectionString())

        ' To keep the example simple, all three asynchronous 
        ' processes select a row from the same table. WAITFOR
        ' commands are used to emulate long-running processes
        ' that complete after different periods of time.
        Dim commandText1 As String = _
         "UPDATE Production.Product " & _
         "SET ReorderPoint = ReorderPoint + 1 " & _
         "WHERE ReorderPoint Is Not Null;" & _
         "WAITFOR DELAY '0:0:01';" & _
         "UPDATE Production.Product " & _
         "SET ReorderPoint = ReorderPoint - 1 " & _
         "WHERE ReorderPoint Is Not Null"

        Dim commandText2 As String = _
         "UPDATE Production.Product " & _
         "SET ReorderPoint = ReorderPoint + 1 " & _
         "WHERE ReorderPoint Is Not Null;" & _
         "WAITFOR DELAY '0:0:05';" & _
         "UPDATE Production.Product " & _
         "SET ReorderPoint = ReorderPoint - 1 " & _
         "WHERE ReorderPoint Is Not Null"

        Dim commandText3 As String = _
         "UPDATE Production.Product " & _
         "SET ReorderPoint = ReorderPoint + 1 " & _
         "WHERE ReorderPoint Is Not Null;" & _
         "WAITFOR DELAY '0:0:10';" & _
         "UPDATE Production.Product " & _
         "SET ReorderPoint = ReorderPoint - 1 " & _
         "WHERE ReorderPoint Is Not Null"

        Dim waitHandles(2) As WaitHandle

        Try
            ' For each process, open a connection and begin execution.
            ' Use the IAsyncResult object returned by 
            ' BeginExecuteReader to add a WaitHandle for the process
            ' to the array.
            connection1.Open()
            Dim command1 As New SqlCommand(commandText1, connection1)
            Dim result1 As IAsyncResult = _
             command1.BeginExecuteNonQuery()
            waitHandles(0) = result1.AsyncWaitHandle

            connection2.Open()
            Dim command2 As New SqlCommand(commandText2, connection2)
            Dim result2 As IAsyncResult = _
             command2.BeginExecuteNonQuery()
            waitHandles(1) = result2.AsyncWaitHandle

            connection3.Open()
            Dim command3 As New SqlCommand(commandText3, connection3)
            Dim result3 As IAsyncResult = _
             command3.BeginExecuteNonQuery()
            waitHandles(2) = result3.AsyncWaitHandle

            ' WaitAll waits for all of the processes to complete.
            ' The return value is True if all processes completed, 
            ' False if any process timed out.
            Dim result As Boolean = _
             WaitHandle.WaitAll(waitHandles, 60000, False)
            If result Then
                Dim rowCount1 As Long = _
                 command1.EndExecuteNonQuery(result1)
                TextBox1.Text = _
                 "Completed " & _
                 System.DateTime.Now.ToLongTimeString()

                Dim rowCount2 As Long = _
                 command2.EndExecuteNonQuery(result2)
                TextBox2.Text = _
                 "Completed " & _
                 System.DateTime.Now.ToLongTimeString()

                Dim rowCount3 As Long = _
                 command3.EndExecuteNonQuery(result3)
                TextBox3.Text = _
                 "Completed " & _
                 System.DateTime.Now.ToLongTimeString()
            Else
                Throw New Exception("Timeout")
            End If
        Catch ex As Exception
            TextBox4.Text = ex.ToString
        End Try
        connection1.Close()
        connection2.Close()
        connection3.Close()

    End Sub

[C#]

// Add the following using statements, if they are not already there.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Threading;
using System.Data.SqlClient;

// Add this code to the page's class
string GetConnectionString()
    //  To avoid storing the connection string in your code,            
    //  you can retrieve it from a configuration file. 
    //  If you have not included "Asynchronous Processing=true" 
    //  in the connection string, the command will not be able
    //  to execute asynchronously.
{
    return "Data Source=(local);Integrated Security=SSPI;" +
        "Initial Catalog=AdventureWorks;" +
        "Asynchronous Processing=true";
}
void Button1_Click(object sender, System.EventArgs e)
{
    //  In a real-world application, you might be connecting to 
    //   three different servers or databases. For the example,
    //   we connect to only one.
    
    SqlConnection connection1 = 
        new SqlConnection(GetConnectionString());
    SqlConnection connection2 = 
        new SqlConnection(GetConnectionString());
    SqlConnection connection3 = 
        new SqlConnection(GetConnectionString());
    //  To keep the example simple, all three asynchronous 
    //  processes execute UPDATE queries that result in
      //  no change to the data. WAITFOR
    //  commands are used to emulate long-running processes
    //  that complete after different periods of time.

    string commandText1 = 
        "UPDATE Production.Product " +
        "SET ReorderPoint = ReorderPoint + 1 " +
        "WHERE ReorderPoint Is Not Null;" +
        "WAITFOR DELAY '0:0:01';" +
        "UPDATE Production.Product " +
        "SET ReorderPoint = ReorderPoint - 1 " +
        "WHERE ReorderPoint Is Not Null";

    string commandText2 = 
      "UPDATE Production.Product " +
      "SET ReorderPoint = ReorderPoint + 1 " +
      "WHERE ReorderPoint Is Not Null;" +
      "WAITFOR DELAY '0:0:05';" +
      "UPDATE Production.Product " +
      "SET ReorderPoint = ReorderPoint - 1 " +
      "WHERE ReorderPoint Is Not Null";

    string commandText3 =
       "UPDATE Production.Product " +
       "SET ReorderPoint = ReorderPoint + 1 " +
       "WHERE ReorderPoint Is Not Null;" +
       "WAITFOR DELAY '0:0:10';" +
       "UPDATE Production.Product " +
       "SET ReorderPoint = ReorderPoint - 1 " +
       "WHERE ReorderPoint Is Not Null";
    try
        //  For each process, open a connection and begin 
        //  execution. Use the IAsyncResult object returned by 
        //  BeginExecuteReader to add a WaitHandle for the 
        //  process to the array.
    {
        connection1.Open();
        SqlCommand command1 =
            new SqlCommand(commandText1, connection1);
        IAsyncResult result1 = command1.BeginExecuteNonQuery();
        WaitHandle waitHandle1 = result1.AsyncWaitHandle;
        connection2.Open();

        SqlCommand command2 =
            new SqlCommand(commandText2, connection2);
        IAsyncResult result2 = command2.BeginExecuteNonQuery();
        WaitHandle waitHandle2 = result2.AsyncWaitHandle;
        connection3.Open();

        SqlCommand command3 =
            new SqlCommand(commandText3, connection3);
        IAsyncResult result3 = command3.BeginExecuteNonQuery();
        WaitHandle waitHandle3 = result3.AsyncWaitHandle;

        WaitHandle[] waitHandles = {
            waitHandle1, waitHandle2, waitHandle3
        };

        bool result;
        //  WaitAll waits for all of the processes to 
        //  complete. The return value is True if the processes
        //  all completed successfully, False if any process
        //  timed out.

        result = WaitHandle.WaitAll(waitHandles, 60000, false);
        if(result)
        {
            long rowCount1 = 
                command1.EndExecuteNonQuery(result1);
            TextBox1.Text = "Completed " +
                System.DateTime.Now.ToLongTimeString();
            long rowCount2 = 
                command2.EndExecuteNonQuery(result2);
            TextBox2.Text = "Completed " +
                System.DateTime.Now.ToLongTimeString();
        
            long rowCount3 = 
                command3.EndExecuteNonQuery(result3);
            TextBox3.Text = "Completed " +
                System.DateTime.Now.ToLongTimeString();
        }
        else
        {
            throw new Exception("Timeout");
        }
    }
        
    catch (Exception ex)
    {
        TextBox4.Text = ex.ToString();
    }
    connection1.Close();
    connection2.Close();
    connection3.Close();
}

Vea también

Otros recursos

Operaciones asincrónicas (ADO.NET)