Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
- Artículo anterior:Paso 3: Prueba de concepto de la conexión a SQL mediante ADO.NET
Este artículo ofrece un ejemplo de código en C# que demuestra la lógica de reintentos implementada manualmente. La lógica de reintento gestiona errores temporales o fallos transitorios, que podrían desaparecer si el programa espera e intenta la operación de nuevo.
Importante
Para las nuevas aplicaciones que usan Microsoft.Data.SqlClient 3.0 y versiones posteriores, es preferible usar la lógica de reintento configurable del driver en lugar de implementar un bucle de reintento. Empieza con Configurar lógica de reintentos en SqlClient.
Estos son algunos de los orígenes de los errores transitorios:
- Un breve fallo de la red que soporta Internet.
- Un sistema en la nube podría estar equilibrando sus recursos cuando llega tu consulta.
Las clases ADO.NET para conectarse a SQL Server también pueden conectarse a Azure SQL Database. Tu programa cliente puede encontrarse con fallos transitorios. Debería recuperarse utilizando una política de reintentos acotados que sea adecuada para la operación.
Paso 1: Identificar los errores transitorios
Tu programa debe distinguir entre errores transitorios y errores persistentes. Los errores transitorios son condiciones de error que pueden desaparecer en poco tiempo, como problemas de red transitoria. Un ejemplo de error persistente es una falta de ortografía del nombre de la base de datos de destino. En ese caso, el error "No se encontró ninguna base de datos de este tipo" persiste y no se resuelve rápidamente.
La lista de números de error categorizados como fallos transitorios está disponible en Mensajes de error para aplicaciones cliente de base de datos SQL.
Paso 2: Crear y ejecutar la aplicación de ejemplo
En el ejemplo se da por supuesto que está instalado .NET Framework 4.6.2 o posterior. El ejemplo de código C# consiste en un archivo llamado Program.cs. En la sección siguiente se proporciona su código.
Paso 2.a: Capturar y compilar el ejemplo de código
Puede compilar el ejemplo con los pasos siguientes:
En la edición gratuita de Visual Studio Community, cree un nuevo proyecto desde la plantilla de aplicación de consola de C#.
Selecciona Archivo>Nuevo>Proyecto>Instalado>Plantillas>Visual C#>Windows>Escritorio clásico>Aplicación de consola.
Asigne al proyecto el nombre
RetryAdo2.
Abra el panel Explorador de soluciones.
- Vea el nombre de su proyecto.
- En el proyecto, añada una dependencia NuGet del paquete Microsoft.Data.SqlClient.
- Consulta el nombre del
Program.csarchivo.
Abra el archivo
Program.cs.Sustituye todo el contenido del
Program.csarchivo por el código del siguiente bloque.Selecciona el menú Build>Build Solution.
Paso 2.b: Copiar y pegar el ejemplo de código
Pega este código en tu Program.cs archivo.
Luego, edita las cadenas para el nombre del servidor y la contraseña. Puedes encontrar estas cadenas en el método llamado GetSqlConnectionString.
Nota:
La cadena de conexión está orientada a Azure SQL Database, porque incluye el prefijo tcp:de cuatro caracteres . Pero puedes ajustar la cadena del servidor para conectarla a tu instancia de SQL Server.
using System;
using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using System.Threading;
namespace RetryAdo2;
public class Program
{
public static int Main(string[] args)
{
bool succeeded = false;
const int totalNumberOfTimesToTry = 4;
int retryIntervalSeconds = 10;
for (int tries = 1; tries <= totalNumberOfTimesToTry; tries++)
{
try
{
if (tries > 1)
{
Console.WriteLine(
"Transient error encountered. Will begin attempt number {0} of {1} max...",
tries,
totalNumberOfTimesToTry
);
Thread.Sleep(1000 * retryIntervalSeconds);
retryIntervalSeconds = Convert.ToInt32(retryIntervalSeconds * 1.5);
}
AccessDatabase();
succeeded = true;
break;
}
catch (SqlException sqlExc) {
if (TransientErrorNumbers.Contains(sqlExc.Number))
{
Console.WriteLine("{0}: transient occurred.", sqlExc.Number);
continue;
}
Console.WriteLine(sqlExc);
break;
}
catch (TestSqlException sqlExc) {
if (TransientErrorNumbers.Contains(sqlExc.Number))
{
Console.WriteLine("{0}: transient occurred. (TESTING.)", sqlExc.Number);
continue;
}
Console.WriteLine(sqlExc);
break;
}
catch (Exception e)
{
Console.WriteLine(e);
break;
}
}
if (!succeeded) {
Console.WriteLine("ERROR: Unable to access the database!");
return 1;
}
return 0;
}
/// <summary>
/// Connects to the database, reads,
/// prints results to the console.
/// </summary>
static void AccessDatabase() {
//throw new TestSqlException(4060); //(7654321); // Uncomment for testing.
using var sqlConnection = new SqlConnection(GetSqlConnectionString());
using var dbCommand = sqlConnection.CreateCommand();
dbCommand.CommandText = @"
SELECT TOP 3
ob.name,
CAST(ob.object_id as NVARCHAR(32)) AS [object_id]
FROM sys.objects AS ob
WHERE ob.type='IT'
ORDER BY ob.name;";
sqlConnection.Open();
var dataReader = dbCommand.ExecuteReader();
while (dataReader.Read())
{
Console.WriteLine(
"{0}\t{1}",
dataReader.GetString(0),
dataReader.GetString(1)
);
}
}
/// <summary>
/// Edit the four string values in accordance with your environment.
/// </summary>
/// <returns>An ADO.NET connection string.</returns>
static private string GetSqlConnectionString()
{
// Prepare the connection string to Azure SQL Database.
var sqlConnectionSB = new SqlConnectionStringBuilder
{
// Change these values to your values.
DataSource = "tcp:myazuresqldbserver.database.windows.net,1433", //["Server"]
InitialCatalog = "MyDatabase", //["Database"]
UserID = "MyLogin", // "@yourservername" as suffix sometimes.
Password = "<password>",
// Adjust these values if you like. (ADO.NET 4.5.1 or later.)
ConnectRetryCount = 3,
ConnectRetryInterval = 10, // Seconds.
// Leave these values as they are.
IntegratedSecurity = false,
Encrypt = true,
ConnectTimeout = 30
};
return sqlConnectionSB.ToString();
}
static List<int> TransientErrorNumbers = new()
{
4060, 40197, 40501, 40613, 49918, 49919, 49920, 11001
};
}
/// <summary>
/// For testing retry logic, you can have method
/// AccessDatabase start by throwing a new
/// TestSqlException with a Number that does
/// or does not match a transient error number
/// present in TransientErrorNumbers.
/// </summary>
internal class TestSqlException : ApplicationException
{
internal TestSqlException(int testErrorNumber)
{
Number = testErrorNumber;
}
internal int Number { get; set; }
}
Paso 2.c: Ejecución del programa
El RetryAdo2.exe ejecutable no toma parámetros. Para ejecutar el .exe:
- Abre una ventana de consola en la carpeta donde compilaste el
RetryAdo2.exebinario. - Ejecuta
RetryAdo2.exesin parámetros de entrada.
database_firewall_rules_table 245575913
filestream_tombstone_2073058421 2073058421
filetable_updates_2105058535 2105058535
Paso 3: Formas de probar la lógica de reintento
Puedes simular un error transitorio para probar tu lógica de reintento de varias maneras.
Paso 3.a: Producir una excepción de prueba
El ejemplo de código incluye:
- Una pequeña segunda clase nombrada
TestSqlExceptioncon una propiedad llamadaNumber. -
//throw new TestSqlException(4060);, que puedes descomentar.
Si descomentas la instrucción `throw` y vuelves a compilar, la siguiente ejecución de RetryAdo2.exe genera una salida similar a la siguiente.
[C:\VS15\RetryAdo2\RetryAdo2\bin\Debug\]
>> RetryAdo2.exe
4060: transient occurred. (TESTING.)
Transient error encountered. Will begin attempt number 2 of 4 max...
4060: transient occurred. (TESTING.)
Transient error encountered. Will begin attempt number 3 of 4 max...
4060: transient occurred. (TESTING.)
Transient error encountered. Will begin attempt number 4 of 4 max...
4060: transient occurred. (TESTING.)
ERROR: Unable to access the database!
[C:\VS15\RetryAdo2\RetryAdo2\bin\Debug\]
>>
Paso 3.b: Volver a probar con un error persistente
Para demostrar que el código gestiona correctamente los errores persistentes, se vuelve a ejecutar la prueba anterior. En lugar de usar un número de error transitorio real como 4060, usa el número ficticio 7654321. El programa trata este número como un error persistente y evita cualquier reintento.
Paso 3.c: Desconectarse de la red
- Desconecte el equipo cliente de la red.
- En el caso de un equipo de escritorio, desconecte el cable de red.
- En el caso de un equipo portátil, presione la combinación de función de teclas para desactivar el adaptador de red.
- Arranca
RetryAdo2.exey espera a que la consola muestre el primer error transitorio, probablemente 11001. - Reconéctate a la red, mientras
RetryAdo2.exesigue funcionando. - Vea cómo la consola indica que la operación se ha realizado correctamente en un reintento posterior.
Paso 3.d: Escribir mal temporalmente el nombre del servidor
- Añadir temporalmente 40615 como otro número de error a
TransientErrorNumbers, y recompilar. - Establezca un punto de interrupción en la línea:
new QC.SqlConnectionStringBuilder(). - Use la característica Editar y continuar para escribir mal deliberadamente el nombre del servidor un par de líneas por debajo.
- Deje que el programa se ejecute y vuelva al punto de interrupción.
- Se produce el error 40615.
- Corrija el error ortográfico.
- Deje que el programa se ejecute y finalice correctamente.
- Quite 40615 y vuelva a compilar.