Resistencia de la conexión e intercepción de comandos de Formularios Web Forms de ASP.NET

por Erik Reitan

En este tutorial, modificará la aplicación de ejemplo Wingtip Toys para que sea compatible con la resistencia de conexión y la intercepción de comandos. Al habilitar la resistencia de conexión, la aplicación de ejemplo Wingtip Toys reintentará automáticamente las llamadas de datos cuando se produzcan errores transitorios propios de un entorno en la nube. Además, con la ejecución de la intercepción de comandos, la aplicación de ejemplo detecta las consultas SQL enviadas a la base de datos para registrarlas o modificarlas.

Nota:

Este tutorial de Web Forms se basa en el siguiente tutorial de MVC de Tom Dykstra:
Resistencia a la conexión e intercepción de comandos con Entity Framework en una aplicación MVC de ASP.NET

Temas que se abordarán:

  • Cómo brindar resistencia de conexión.
  • Cómo ejecutar la interceptación de comandos.

Requisitos previos

Antes de empezar, asegúrese de que el software siguiente está instalado en el equipo:

Resistencia de la conexión

Cuando se plantee la posibilidad de implementar una aplicación en Windows Azure, una opción que se debe tener en cuenta es implementar la base de datos en WindowsAzure SQL Database, un servicio de base de datos en la nube. Los errores de conexión transitorios suelen ser más frecuentes cuando se conecta a un servicio de base de datos en la nube que cuando el servidor web y el servidor de bases de datos están conectados directamente en el mismo centro de datos. Incluso si un servidor web en la nube y un servicio de base de datos en la nube se hospedan en el mismo centro de datos, hay más conexiones de red entre ellos que pueden tener problemas, como equilibradores de carga.

Además, otros usuarios suelen compartir un servicio en la nube, lo que significa que su capacidad de respuesta puede verse afectada por ellos. Y es posible que el acceso a la base de datos esté sujeto a limitaciones. La limitación significa que el servicio de base de datos produce excepciones al intentar acceder a él con más frecuencia de lo permitido en el Acuerdo de Nivel de Servicio (SLA).

Muchos o la mayoría de los problemas de conexión que tienen lugar al acceder a un servicio en la nube son transitorios, es decir, se resuelven en un breve período de tiempo. Por lo tanto, al intentar una operación de base de datos y obtener un tipo de error que suele ser transitorio, podría intentar la operación de nuevo después de una breve espera y la operación podría ser correcta. Puede ofrecer a los usuarios una experiencia mucho mejor si controla errores transitorios mediante un nuevo intento automático, con lo que se logra que la mayoría de ellos sean invisibles para el cliente. La característica de resistencia de conexión de Entity Framework 6 automatiza ese proceso de reintento de consultas SQL con errores.

La característica de resistencia de conexión debe configurarse adecuadamente para un servicio de base de datos determinado:

  1. Tiene que saber qué excepciones es probable que sean transitorias. Quiere reintentar errores causados por una pérdida temporal en la conectividad de red, no errores causados por errores de programa, por ejemplo.
  2. Tiene que esperar una cantidad de tiempo adecuada entre reintentos de una operación con error. Puede esperar más tiempo entre reintentos para un proceso por lotes que para una página web en línea en la que un usuario está esperando una respuesta.
  3. Tiene que reintentar un número adecuado de veces antes de renunciar. Es posible que quiera volver a intentarlo más veces en un proceso por lotes que lo haría en una aplicación en línea.

Puede configurar estas opciones manualmente para cualquier entorno de base de datos compatible con un proveedor de Entity Framework.

Todo lo que tiene que hacer para habilitar la resistencia de conexión es crear una clase en el ensamblado que deriva de la clase DbConfiguration y, en esa clase, establezca la estrategia de ejecución de SQL Database, que en EF es otro término para directiva de reintentos.

Ejecución de la resistencia de conexión

  1. Descargue y abra la aplicación Web Forms de ejemplo WingtipToysen Visual Studio.

  2. En la carpeta Logic de la aplicación WingtipToys, agregue un archivo de clase denominado WingtipToysConfiguration.cs.

  3. Reemplace el código existente con el siguiente:

    using System.Data.Entity;
    using System.Data.Entity.SqlServer;
     
    namespace WingtipToys.Logic
    {
        public class WingtipToysConfiguration : DbConfiguration
        {
            public WingtipToysConfiguration()
            {
              SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
            }
        }
    }
    

Entity Framework ejecuta automáticamente el código que encuentra en una clase que deriva de DbConfiguration. Puede usar la clase DbConfiguration para realizar tareas de configuración en el código que haría en el archivo Web.config. Para obtener más información, consulte EntityFramework Code-Based Configuration.

  1. En la carpeta Logic, abra el archivo AddProducts.cs.

  2. Agregue una instrucción using para System.Data.Entity.Infrastructure, como se muestra en la sección resaltada en amarillo:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using WingtipToys.Models;
    using System.Data.Entity.Infrastructure;
    
  3. Agregue un bloque catch al método AddProduct para que RetryLimitExceededException se registre, como se muestra en la sección resaltada en amarillo:

    public bool AddProduct(string ProductName, string ProductDesc, string ProductPrice, string ProductCategory, string ProductImagePath)
    {
        var myProduct = new Product();
        myProduct.ProductName = ProductName;
        myProduct.Description = ProductDesc;
        myProduct.UnitPrice = Convert.ToDouble(ProductPrice);
        myProduct.ImagePath = ProductImagePath;
        myProduct.CategoryID = Convert.ToInt32(ProductCategory);
    
        using (ProductContext _db = new ProductContext())
        {
            // Add product to DB.
            _db.Products.Add(myProduct);
            try
            {
                _db.SaveChanges();
            }
            catch (RetryLimitExceededException ex)
            {
                // Log the RetryLimitExceededException.
                WingtipToys.Logic.ExceptionUtility.LogException(ex, "Error: RetryLimitExceededException -> RemoveProductButton_Click in AdminPage.aspx.cs");
            }
        }
        // Success.
        return true;
    }
    

Al agregar la excepción RetryLimitExceededException, puede brindar un mejor registro o mostrar un mensaje de error al usuario, para que este pueda optar por volver a intentar el proceso. Al detectar la excepción RetryLimitExceededException, ya se habrán probado y habrán fallado varias veces los únicos errores que posiblemente sean transitorios. La excepción real devuelta se ajustará en la excepción RetryLimitExceededException. Además, también ha agregado un bloque catch general. Para obtener más información sobre la excepción RetryLimitExceededException, consulte Resistencia de conexión de Entity Framework/Reintentar lógica.

Intercepción de comandos

Ahora que ha activado una directiva de reintento, ¿cómo se pone a prueba para comprobar que funciona según lo previsto? No es tan fácil forzar que se produzca un error transitorio, especialmente cuando se ejecuta localmente, y sería especialmente difícil integrar errores transitorios reales en una prueba unitaria automatizada. Para probar la característica de resistencia de conexión, necesita una manera de interceptar las consultas que Entity Framework envía a SQL Server y reemplazar la respuesta de SQL Server por un tipo de excepción que suele ser transitorio.

También puede usar la interceptación de consultas para implementar un procedimiento recomendado para las aplicaciones en la nube: registre la latencia y el éxito o el error de todas las llamadas a servicios externos, como los servicios de base de datos.

En esta sección del tutorial, utilizará la función de intercepción de Entity Framework tanto para el registro como para la simulación de errores transitorios.

Creación de una interfaz de registro y una clase

Para el registro, se recomienda proceder mediante el uso de interface en lugar de codificar de forma rígida las llamadas a System.Diagnostics.Trace o una clase de registro. Esto facilita el cambio del mecanismo de registro más adelante si alguna vez necesita hacerlo. Por lo tanto, en esta sección creará la interfaz de registro y una clase para su ejecución.

Según el procedimiento anterior, ha descargado y abierto la aplicación de ejemplo WingtipToys en Visual Studio.

  1. Cree una carpeta en el proyecto WingtipToys y nómbrela como Registro.

  2. En la carpeta Registro, cree un archivo de clase denominado ILogger.csy sustituya el código predeterminado por el código siguiente:

    using System;
     
    namespace WingtipToys.Logging
    {
        public interface ILogger
        {
            void Information(string message);
            void Information(string fmt, params object[] vars);
            void Information(Exception exception, string fmt, params object[] vars);
    
            void Warning(string message);
            void Warning(string fmt, params object[] vars);
            void Warning(Exception exception, string fmt, params object[] vars);
    
            void Error(string message);
            void Error(string fmt, params object[] vars);
            void Error(Exception exception, string fmt, params object[] vars);
    
            void TraceApi(string componentName, string method, TimeSpan timespan);
            void TraceApi(string componentName, string method, TimeSpan timespan, string properties);
            void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars);
    
        }
    }
    

    La interfaz ofrece tres niveles de seguimiento para indicar la importancia relativa de los registros y una diseñada para ofrecer información de latencia para las llamadas de servicio externos, como las consultas de base de datos. Los métodos de registro tienen sobrecargas que permiten pasar una excepción. Esto es para que la clase que ejecuta la interfaz registre de forma confiable la información de excepción, incluidas las excepciones internas y el seguimiento de la pila, en lugar de confiar en que esto suceda en cada llamada al método de registro en toda la aplicación.

    Los métodos TraceApi habilitan realizar un seguimiento de la latencia de cada llamada a un servicio externo, como SQL Database.

  3. En la carpeta Registro, cree un archivo de clase denominado Logger.cs y sustituya el código predeterminado por el código siguiente:

    using System;
    using System.Diagnostics;
    using System.Text;
     
    namespace WingtipToys.Logging
    {
      public class Logger : ILogger
      {
     
        public void Information(string message)
        {
          Trace.TraceInformation(message);
        }
     
        public void Information(string fmt, params object[] vars)
        {
          Trace.TraceInformation(fmt, vars);
        }
     
        public void Information(Exception exception, string fmt, params object[] vars)
        {
          Trace.TraceInformation(FormatExceptionMessage(exception, fmt, vars));
        }
     
        public void Warning(string message)
        {
          Trace.TraceWarning(message);
        }
     
        public void Warning(string fmt, params object[] vars)
        {
          Trace.TraceWarning(fmt, vars);
        }
     
        public void Warning(Exception exception, string fmt, params object[] vars)
        {
          Trace.TraceWarning(FormatExceptionMessage(exception, fmt, vars));
        }
     
        public void Error(string message)
        {
          Trace.TraceError(message);
        }
     
        public void Error(string fmt, params object[] vars)
        {
          Trace.TraceError(fmt, vars);
        }
     
        public void Error(Exception exception, string fmt, params object[] vars)
        {
          Trace.TraceError(FormatExceptionMessage(exception, fmt, vars));
        }
     
        public void TraceApi(string componentName, string method, TimeSpan timespan)
        {
          TraceApi(componentName, method, timespan, "");
        }
     
        public void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars)
        {
          TraceApi(componentName, method, timespan, string.Format(fmt, vars));
        }
        public void TraceApi(string componentName, string method, TimeSpan timespan, string properties)
        {
          string message = String.Concat("Component:", componentName, ";Method:", method, ";Timespan:", timespan.ToString(), ";Properties:", properties);
          Trace.TraceInformation(message);
        }
     
        private static string FormatExceptionMessage(Exception exception, string fmt, object[] vars)
        {
          var sb = new StringBuilder();
          sb.Append(string.Format(fmt, vars));
          sb.Append(" Exception: ");
          sb.Append(exception.ToString());
          return sb.ToString();
        }
      }
    }
    

La ejecución usa System.Diagnostics para llevar a cabo el seguimiento. Se trata de una característica integrada de .NET que facilita la generación y el uso de información de seguimiento. Hay muchos «clientes de escucha» que puede usar con el seguimiento System.Diagnostics para escribir registros en archivos, por ejemplo, o para escribirlos en Blob Storage en Azure. Consulte algunas de las opciones y vínculos a otros recursos para obtener más información, en Solución de problemas de sitios web de Windows Azure en Visual Studio. En este tutorial, solo verá los registros en la ventana de salida de Visual Studio.

En una aplicación de producción, es posible que desee considerar el uso de marcos de seguimiento distintos de System.Diagnostics, y la interfaz ILogger facilita el cambio a un mecanismo de seguimiento diferente si decide hacerlo.

Creación de clases de interceptor

A continuación, creará las clases a las que llamará Entity Framework cada vez que va a enviar una consulta a la base de datos, una para simular errores transitorios y otro para realizar el registro. Estas clases de interceptor deben derivarse de la clase DbCommandInterceptor. En ellos se escriben invalidaciones de método a las que se llama automáticamente cuando la consulta está a punto de ejecutarse. En estos métodos, puede examinar o registrar la consulta que se envía a la base de datos y puede cambiar la consulta antes de que se envíe a la base de datos o devolver algo a Entity Framework usted mismo sin pasar la consulta a la base de datos.

  1. Para crear la clase interceptor que registrará todas las consultas SQL que se envían a la base de datos, cree un nombre de archivo de clase InterceptorLogging.cs en la carpeta Logic y sustituya el código de plantilla por el código siguiente:

    using System;
    using System.Data.Common;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure.Interception;
    using System.Data.Entity.SqlServer;
    using System.Data.SqlClient;
    using System.Diagnostics;
    using System.Reflection;
    using System.Linq;
    using WingtipToys.Logging;
    
    namespace WingtipToys.Logic
    {
      public class InterceptorLogging : DbCommandInterceptor
      {
        private ILogger _logger = new Logger();
        private readonly Stopwatch _stopwatch = new Stopwatch();
    
        public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
          base.ScalarExecuting(command, interceptionContext);
          _stopwatch.Restart();
        }
    
        public override void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
          _stopwatch.Stop();
          if (interceptionContext.Exception != null)
          {
            _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
          }
          else
          {
            _logger.TraceApi("SQL Database", "Interceptor.ScalarExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
          }
          base.ScalarExecuted(command, interceptionContext);
        }
    
        public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
          base.NonQueryExecuting(command, interceptionContext);
          _stopwatch.Restart();
        }
    
        public override void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
        {
          _stopwatch.Stop();
          if (interceptionContext.Exception != null)
          {
            _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
          }
          else
          {
            _logger.TraceApi("SQL Database", "Interceptor.NonQueryExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
          }
          base.NonQueryExecuted(command, interceptionContext);
        }
    
        public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
          base.ReaderExecuting(command, interceptionContext);
          _stopwatch.Restart();
        }
        public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
          _stopwatch.Stop();
          if (interceptionContext.Exception != null)
          {
            _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
          }
          else
          {
            _logger.TraceApi("SQL Database", "Interceptor.ReaderExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
          }
          base.ReaderExecuted(command, interceptionContext);
        }
      }
    }
    

    Para las consultas o comandos correctos, este código escribe un registro de información con información de latencia. En el caso de las excepciones, crea un registro de errores.

  2. Para crear la clase interceptor que generará errores transitorios ficticios al escribir "Throw" en el cuadro de nombre en la página denominada AdminPage.aspx, cree un nombre de archivo de clase denominado InterceptorTransientErrors.cs en la carpeta Logic y sustituya el código predeterminado por el código siguiente:

    using System;
    using System.Data.Common;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure.Interception;
    using System.Data.Entity.SqlServer;
    using System.Data.SqlClient;
    using System.Diagnostics;
    using System.Reflection;
    using System.Linq;
    using WingtipToys.Logging;
     
    namespace WingtipToys.Logic
    {
      public class InterceptorTransientErrors : DbCommandInterceptor
      {
        private int _counter = 0;
        private ILogger _logger = new Logger();
     
        public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
          bool throwTransientErrors = false;
          if (command.Parameters.Count > 0 && command.Parameters[0].Value.ToString() == "Throw")
          {
            throwTransientErrors = true;
            command.Parameters[0].Value = "TransientErrorExample";
            command.Parameters[1].Value = "TransientErrorExample";
          }
     
          if (throwTransientErrors && _counter < 4)
          {
            _logger.Information("Returning transient error for command: {0}", command.CommandText);
            _counter++;
            interceptionContext.Exception = CreateDummySqlException();
          }
        }
     
        private SqlException CreateDummySqlException()
        {
          // The instance of SQL Server you attempted to connect to does not support encryption
          var sqlErrorNumber = 20;
     
          var sqlErrorCtor = typeof(SqlError).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 7).Single();
          var sqlError = sqlErrorCtor.Invoke(new object[] { sqlErrorNumber, (byte)0, (byte)0, "", "", "", 1 });
     
          var errorCollection = Activator.CreateInstance(typeof(SqlErrorCollection), true);
          var addMethod = typeof(SqlErrorCollection).GetMethod("Add", BindingFlags.Instance | BindingFlags.NonPublic);
          addMethod.Invoke(errorCollection, new[] { sqlError });
     
          var sqlExceptionCtor = typeof(SqlException).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 4).Single();
          var sqlException = (SqlException)sqlExceptionCtor.Invoke(new object[] { "Dummy", errorCollection, null, Guid.NewGuid() });
     
          return sqlException;
        }
      }
    }
    

    Este código solo invalida el método ReaderExecuting, al que se llama para las consultas que pueden devolver varias filas de datos. Si desea comprobar la resistencia de conexión para otros tipos de consultas, también podría invalidar los métodos NonQueryExecuting y ScalarExecuting, como lo hace el interceptor de registro.

    Más adelante, iniciará sesión como "Admin" y seleccionará el vínculo Admin en la barra de navegación superior. A continuación, en la página AdminPage.aspx, agregará un producto denominado "Throw". El código crea una excepción ficticia de SQL Database para el número de error 20, un tipo que se sabe que suele ser transitorio. Otros números de error actualmente reconocidos como transitorios son 64, 233, 10053, 10054, 10060, 10928, 10929, 40197, 40501 y 40613, pero estos están sujetos a cambios en nuevas versiones de SQL Database. El nombre del producto se cambiará a "TransientErrorExample", y usted podrá seguirlo en el código del archivo InterceptorTransientErrors.cs.

    El código devuelve la excepción a Entity Framework en lugar de ejecutar la consulta y devolver los resultados. La excepción transitoria se devuelve cuatro veces y, a continuación, el código vuelve al procedimiento normal de pasar la consulta a la base de datos.

    Dado que todo se registra, podrá ver que Entity Framework intenta ejecutar la consulta cuatro veces antes de que finalmente se realice correctamente y la única diferencia en la aplicación es que tarda más tiempo en representar una página con los resultados de la consulta.

    Número de veces que Entity Framework volverá a intentarlo es configurable; el código especifica cuatro veces porque es el valor predeterminado de la directiva de ejecución de SQL Database. Si cambia la directiva de ejecución, también cambiaría el código aquí que especifica cuántas veces se generan errores transitorios. También puede cambiar el código para generar más excepciones para que Entity Framework inicie la excepción RetryLimitExceededException.

  3. En Global.asax, agregue las siguientes instrucciones de uso:

    using System.Data.Entity.Infrastructure.Interception;
    
  4. Después, agregue las líneas resaltadas al método Application_Start:

    void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on application startup
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    
        // Initialize the product database.
        Database.SetInitializer(new ProductDatabaseInitializer());
    
        // Create administrator role and user.
        RoleActions roleActions = new RoleActions();
        roleActions.createAdmin();
    
        // Add Routes.
        RegisterRoutes(RouteTable.Routes);
    
        // Logging.
        DbInterception.Add(new InterceptorTransientErrors());
        DbInterception.Add(new InterceptorLogging());
      
    }
    

Estas líneas de código son las que hacen que el código del interceptor se ejecute cuando Entity Framework envía consultas a la base de datos. Tenga en cuenta que, dado que ha creado clases de interceptor independientes para la simulación y el registro de errores transitorios, puede habilitarlas y deshabilitarlas de forma independiente.

Puede agregar interceptores mediante el método DbInterception.Add en cualquier parte del código; no tiene que ser en el método Application_Start. Otra opción, si no agrega interceptores en el método Application_Start, consiste en actualizar o agregar la clase denominada WingtipToysConfiguration.cs y colocar el código anterior al final del constructor de la clase WingtipToysConfiguration.

Siempre que coloque este código, tenga cuidado de no ejecutar DbInterception.Add para el mismo interceptor más de una vez u obtendrá instancias adicionales del interceptor. Por ejemplo, si agrega el interceptor de registro dos veces, verá dos registros para cada consulta SQL.

Los interceptores se ejecutan en el orden de registro (el orden en que se llama al método DbInterception.Add). El orden puede ser importante en función de lo que esté haciendo en el interceptor. Por ejemplo, un interceptor podría cambiar el comando SQL que obtiene en la propiedad CommandText. Si cambia el comando SQL, el siguiente interceptor obtendrá el comando SQL cambiado, no el comando SQL original.

Ha escrito el código de simulación de error transitorio de una manera que le permite producir errores transitorios escribiendo un valor diferente en la interfaz de usuario. Como alternativa, podría escribir el código del interceptor para generar siempre la secuencia de excepciones transitorias sin comprobar si hay un valor de parámetro determinado. Después, podría agregar el interceptor solo cuando quiera generar errores transitorios. Sin embargo, si lo hace, no agregue el interceptor hasta que se haya completado la inicialización de la base de datos. En otras palabras, proceda al menos con una operación de base de datos como una consulta en uno de los conjuntos de entidades antes de empezar a generar errores transitorios. Entity Framework ejecuta varias consultas durante la inicialización de la base de datos y no se ejecutan en una transacción, por lo que los errores durante la inicialización podrían provocar que el contexto entre en un estado incoherente.

Prueba de la resistencia de la conexión y del registro

  1. En Visual Studio, pulse F5 para ejecutar la aplicación en modo de depuración y, a continuación, inicie sesión como "Admin", con "Pa$$word" como contraseña.

  2. Seleccione Admin en la barra de navegación de la parte superior.

  3. Escriba un nuevo producto denominado "Throw" con la descripción, el precio y el archivo de imagen adecuados.

  4. Pulse el botón Agregar producto.
    Observará que el explorador parece bloquearse durante varios segundos mientras Entity Framework vuelve a intentar la consulta varias veces. El primer reintento se produce muy rápidamente y, a continuación, la espera aumenta antes de cada reintento adicional. Este proceso de espera más prolongada antes de que se llame a cada reintento se denomina retroceso exponencial.

  5. Espere hasta que la página ya no intente cargarse.

  6. Detenga el proyecto y consulte la ventana de salida de Visual Studio para comprobar la salida de seguimiento. Para encontrar la ventana de salida, seleccione Depurar - >Windows - >Salida. Es posible que tenga que desplazarse más allá de otros registros escritos por el registrador.

    Observe que puede ver las consultas SQL reales enviadas a la base de datos. Verá algunas consultas y comandos iniciales que Entity Framework realiza para empezar, comprobando la versión de la base de datos y la tabla del historial de migración.
    Output Window
    Tenga en cuenta que no puede repetir esta prueba a menos que detenga la aplicación y la reinicie. Si desea poder probar la resistencia de conexión varias veces en una sola ejecución de la aplicación, puede escribir código para restablecer el contador de errores en InterceptorTransientErrors.

  7. Para ver la diferencia en la que se traduce la estrategia de ejecución (directiva de reintento), convierta en comentario la línea SetExecutionStrategy en el archivo WingtipToysConfiguration.cs en la carpeta Logic, vuelva a ejecutar la página Admin en modo de depuración y agregue de nuevo el producto denominado "Throw".

    Esta vez, el depurador se detiene en la primera excepción generada inmediatamente cuando intenta ejecutar la consulta la primera vez.
    Debugging - View Detail

  8. Quite la marca de comentario de la línea SetExecutionStrategy en el archivo WingtipToysConfiguration.cs.

Resumen

En este tutorial, ha aprendido a modificar una aplicación Web Forms de ejemplo para que sea compatible con la resistencia de conexión y la intercepción de comandos.

Pasos siguientes

Después de revisar la resistencia de conexión y la intercepción de comandos en ASP.NET Web Forms, revise el tema Métodos asincrónicos en ASP.NET 4.5 de ASP.NET Web Forms. Con este tema, aprenderá las nociones básicas de construir una aplicación asincrónica de ASP.NET Web Forms con Visual Studio.