Tutorial: Uso de la resistencia de conexión y la interceptación de comandos con Entity Framework en una aplicación de MVC de ASP.NET
Hasta ahora, la aplicación se ha ejecutado localmente en IIS Express en el proceso de desarrollo. Para que una aplicación real esté disponible para que otras personas lo usen a través de Internet, debe implementarla en un proveedor de hospedaje web y debe implementar la base de datos en un servidor de bases de datos.
En este tutorial aprenderá a usar la resistencia de conexión y la interceptación de comandos. Son dos características importantes de Entity Framework 6 que son especialmente valiosas cuando se implementa en el entorno de nube: resistencia de conexión (reintentos automáticos para errores transitorios) y interceptación de comandos (captura todas las consultas SQL enviadas a la base de datos para registrarlas o cambiarlas).
Este tutorial de resistencia y interceptación de comandos de conexión es opcional. Si omite este tutorial, algunos ajustes menores tendrán que realizarse en tutoriales posteriores.
En este tutorial, hizo lo siguiente:
- Habilitación de la resistencia de la conexión
- Habilitación de la interceptación de comandos
- Prueba de la nueva configuración
Requisitos previos
Habilitación de la resistencia de la conexión
Al implementar la aplicación en Windows Azure, implementará la base de datos en Windows Azure 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 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 proporcionar una experiencia mucho mejor para los usuarios si controla errores transitorios intentando de nuevo automáticamente, lo que hace 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:
- 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.
- 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.
- 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, pero los valores predeterminados que normalmente funcionan bien para una aplicación en línea que usa Windows Azure SQL Database ya se han configurado para usted y esas son las opciones que implementará para la aplicación Contoso University.
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 laestrategia de ejecución de SQL Database, que en EF es otro término para directiva de reintento.
En la carpeta DAL, agregue un archivo de clase denominado SchoolConfiguration.cs.
Reemplace el código de plantilla por el código siguiente:
using System.Data.Entity; using System.Data.Entity.SqlServer; namespace ContosoUniversity.DAL { public class SchoolConfiguration : DbConfiguration { public SchoolConfiguration() { 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 claseDbConfiguration
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.En StudentController.cs, agregue una
using
instrucción paraSystem.Data.Entity.Infrastructure
.using System.Data.Entity.Infrastructure;
Cambie todos los
catch
bloques que detectanDataException
excepciones para que capturenRetryLimitExceededException
excepciones en su lugar. Por ejemplo:catch (RetryLimitExceededException /* dex */) { //Log the error (uncomment dex variable name and add a line here to write a log. ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator."); }
Estaba usando
DataException
para intentar identificar errores que podrían ser transitorios con el fin de proporcionar un mensaje descriptivo "inténtelo de nuevo". Pero ahora que ha activado una directiva de reintento, los únicos errores que probablemente sean transitorios ya se habrán probado y fallado varias veces y la excepción real devuelta se ajustará en laRetryLimitExceededException
excepción.
Para obtener más información, consulte Resistencia de conexión de Entity Framework/Reintentar lógica.
Habilitación de la interceptación de comandos
Ahora que ha activado una directiva de reintento, ¿cómo se 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. EF6 proporciona una API de registro dedicada que puede facilitar el registro, pero en esta sección del tutorial aprenderá a usar la característica de interceptación de Entity Framework directamente, tanto para el registro como para simular errores transitorios.
Creación de una interfaz de registro y una clase
Un procedimiento recomendado para el registro es hacerlo mediante una interfaz en lugar de llamar de codificación rígida a System.Diagnostics.Trace o a 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 implementarla./p>
Cree una carpeta en el proyecto y asígnela el nombre Registro.
En la carpeta Registro, cree un archivo de clase denominado ILogger.csy reemplace el código de plantilla por el código siguiente:
using System; namespace ContosoUniversity.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 proporciona tres niveles de seguimiento para indicar la importancia relativa de los registros y una diseñada para proporcionar 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 implemente la interfaz registra 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 se realiza 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.
En la carpeta Registro, cree un archivo de clase denominado Student.cs y reemplace el código de plantilla con el código siguiente:
using System; using System.Diagnostics; using System.Text; namespace ContosoUniversity.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) { // Simple exception formatting: for a more comprehensive version see // https://code.msdn.microsoft.com/windowsazure/Fix-It-app-for-Building-cdd80df4 var sb = new StringBuilder(); sb.Append(string.Format(fmt, vars)); sb.Append(" Exception: "); sb.Append(exception.ToString()); return sb.ToString(); } } }
La implementación usa System.Diagnostics para realizar 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 de 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 Azure en Visual Studio. En este tutorial, solo verá los registros en la ventanaSalida de Visual Studio.
En una aplicación de producción, es posible que desee considerar la posibilidad de realizar el seguimiento de paquetes 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 DbCommandInterceptor
clase. 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.
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 SchoolInterceptorLogging.cs en la carpeta DAL y reemplace 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 ContosoUniversity.Logging; namespace ContosoUniversity.DAL { public class SchoolInterceptorLogging : 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", "SchoolInterceptor.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", "SchoolInterceptor.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", "SchoolInterceptor.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.
Para crear la clase interceptor que generará errores transitorios ficticios al escribir "Throw" en el cuadro Buscar, cree un nombre de archivo de clase denominado SchoolInterceptorTransientErrors.cs en la carpeta DAL y reemplace 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 ContosoUniversity.Logging; namespace ContosoUniversity.DAL { public class SchoolInterceptorTransientErrors : 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 = "%an%"; command.Parameters[1].Value = "%an%"; } 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
ReaderExecuting
método, 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 losNonQueryExecuting
métodos yScalarExecuting
, como lo hace el interceptor de registro.Al ejecutar la página Student y escribir "Throw" como cadena de búsqueda, este código crea una excepción ficticia de SQL Database para el número de error 20, un tipo conocido como normalmente 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 código devuelve la excepción a Entity Framework en lugar de ejecutar la consulta y devolver los resultados de la consulta. 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
RetryLimitExceededException
excepción.El valor que escriba en el cuadro Buscar estará en
command.Parameters[0]
ycommand.Parameters[1]
(se usa para el nombre y otro para el apellido). Cuando se encuentra el valor "%Throw%", "Throw" se reemplaza en esos parámetros por "an" para que se encuentren y devuelvan algunos alumnos.Esta es simplemente una manera cómoda de probar la resistencia de la conexión en función de cambiar alguna entrada a la interfaz de usuario de la aplicación. También puede escribir código que genere errores transitorios para todas las consultas o actualizaciones, como se explica más adelante en los comentarios sobre el método DbInterception.Add.
En Global.asax, agregue las siguientes instrucciones
using
:using ContosoUniversity.DAL; using System.Data.Entity.Infrastructure.Interception;
Agregue las líneas resaltadas al
Application_Start
método :protected void Application_Start() { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); DbInterception.Add(new SchoolInterceptorTransientErrors()); DbInterception.Add(new SchoolInterceptorLogging()); }
Estas líneas de código son lo que hace 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
DbInterception.Add
método en cualquier parte del código; no tiene que estar en elApplication_Start
método. Otra opción es colocar este código en la clase DbConfiguration que creó anteriormente para configurar la directiva de ejecución.public class SchoolConfiguration : DbConfiguration { public SchoolConfiguration() { SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy()); DbInterception.Add(new SchoolInterceptorTransientErrors()); DbInterception.Add(new SchoolInterceptorLogging()); } }
Siempre que coloque este código, tenga cuidado de no ejecutarse
DbInterception.Add
para el mismo interceptor más de una vez o 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 importar 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 propiedadCommandText
. 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, realice al menos 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 nueva configuración
Presione F5 para ejecutar la aplicación en modo de depuración y, a continuación, haga clic en la pestaña Estudiantes.
Examine la ventana Salidade Visual Studio para ver la salida de seguimiento. Es posible que tenga que desplazarse más allá de algunos errores de JavaScript para acceder a los 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 (obtendrá información sobre las migraciones en el siguiente tutorial). Y verá una consulta para la paginación, para averiguar cuántos alumnos hay y, por último, verá la consulta que obtiene los datos de los alumnos.
En la página Estudiante, escriba "Throw" (Iniciar) como cadena de búsqueda y haga clic en Buscar.
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 antes de aumentar antes de cada reintento adicional. Este proceso de espera más tiempo antes de que se llame a cada reintento retroceso exponencial.
Cuando se muestre la página, mostrando a los alumnos que tienen "an" en sus nombres, examine la ventana de salida y verá que se intentó la misma consulta cinco veces, las primeras cuatro veces que devuelven excepciones transitorias. Para cada error transitorio, verá el registro que escribe al generar el error transitorio en la clase
SchoolInterceptorTransientErrors
("Devolver error transitorio para el comando...") y verá el registro escrito cuandoSchoolInterceptorLogging
obtiene la excepción.Puesto que especificó una cadena de búsqueda, la consulta que devuelve los datos de los alumnos se parametriza:
SELECT TOP (3) [Project1].[ID] AS [ID], [Project1].[LastName] AS [LastName], [Project1].[FirstMidName] AS [FirstMidName], [Project1].[EnrollmentDate] AS [EnrollmentDate] FROM ( SELECT [Project1].[ID] AS [ID], [Project1].[LastName] AS [LastName], [Project1].[FirstMidName] AS [FirstMidName], [Project1].[EnrollmentDate] AS [EnrollmentDate], row_number() OVER (ORDER BY [Project1].[LastName] ASC) AS [row_number] FROM ( SELECT [Extent1].[ID] AS [ID], [Extent1].[LastName] AS [LastName], [Extent1].[FirstMidName] AS [FirstMidName], [Extent1].[EnrollmentDate] AS [EnrollmentDate] FROM [dbo].[Student] AS [Extent1] WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~') ) AS [Project1] ) AS [Project1] WHERE [Project1].[row_number] > 0 ORDER BY [Project1].[LastName] ASC:
No registra el valor de los parámetros, pero puede hacerlo. Si desea ver los valores de parámetro, puede escribir código de registro para obtener valores de parámetro de la propiedad
Parameters
del objetoDbCommand
que obtiene en los métodos interceptores.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
SchoolInterceptorTransientErrors
.Para ver la diferencia que hace la estrategia de ejecución (directiva de reintento), convierta en comentario la
SetExecutionStrategy
línea de SchoolConfiguration.cs, vuelva a ejecutar la página Students en modo de depuración y busque "Throw" de nuevo.Esta vez el depurador se detiene en la primera excepción generada inmediatamente cuando intenta ejecutar la consulta la primera vez.
Quite la marca de comentario de la línea SetExecutionStrategy en SchoolConfiguration.cs.
Obtención del código
Descargar el proyecto completado
Recursos adicionales
Encontrará vínculos a otros recursos de Entity Framework en Acceso a datos de ASP.NET: Recursos recomendados.
Pasos siguientes
En este tutorial ha:
- Resistencia de conexión habilitada
- Interceptación de comandos habilitada
- Prueba de la nueva configuración
Pase al siguiente artículo para obtener información sobre las migraciones de Code First y la implementación de Azure.