Etapa 4: Conecte-se de forma resiliente ao SQL com o ADO.NET
- Artigo anterior: Etapa 3: Prova de conceito conectando-se ao SQL usando ADO.NET
Este tópico fornece um exemplo de código C# que demonstra a lógica de repetição personalizada. A lógica de repetição fornece confiabilidade. A lógica de repetição é projetada para processar graciosamente erros temporários ou falhas transitórias que tendem a desaparecer se o programa esperar vários segundos e tentar novamente.
As fontes de falhas transitórias incluem:
- Uma breve falha da rede que suporta a Internet.
- Um sistema de nuvem pode estar equilibrando a carga de seus recursos no momento em que sua consulta foi enviada.
As classes ADO.NET para se conectar ao Microsoft SQL Server local também podem se conectar ao Banco de Dados SQL do Azure. No entanto, por si só, as classes ADO.NET não podem fornecer toda a robustez e fiabilidade necessárias na utilização em produção. O seu programa cliente pode encontrar falhas transitórias das quais ele deve recuperar-se automaticamente e de forma tranquila.
Etapa 1: Identificar erros transitórios
Seu programa deve distinguir entre erros transitórios e erros persistentes. Erros transitórios são condições de erro que podem desaparecer dentro de um curto período de tempo, como problemas de rede transitórios. Um exemplo de um erro persistente seria, se o seu programa tem um erro ortográfico do nome do banco de dados de destino - neste caso, o erro "Nenhum banco de dados encontrado" persistiria, e não tem nenhuma chance de limpar dentro de um curto período de tempo.
A lista de números de erro categorizados como falhas transitórias está disponível em Mensagens de erro para aplicativos cliente do Banco de dados SQL
Etapa 2: Criar e executar o aplicativo de exemplo
Este exemplo pressupõe que o .NET Framework 4.6.2 ou posterior esteja instalado. O exemplo de código C# consiste em um arquivo chamado Program.cs. Seu código é fornecido na próxima seção.
Etapa 2.a: Capturar e compilar o exemplo de código
Você pode compilar o exemplo com as seguintes etapas:
- No edição gratuita do Visual Studio Community, crie um novo projeto a partir do modelo Aplicativo de Console do C#.
- Arquivo > Novo Projeto >> Modelos de > instalados > Visual C# > Windows > Classic Desktop > Console Application
- Nomeie o projeto RetryAdo2.
- Abra o painel Gerenciador de Soluções.
- Veja o nome do seu projeto.
- No seu projeto , adicione uma dependência NuGet ao pacote Microsoft.Data.SqlClient.
- Consulte o nome do arquivo Program.cs.
- Abra o arquivo Program.cs.
- Substitua totalmente o conteúdo do arquivo Program.cs com o código no bloco de código a seguir.
- Clique no menu Build > Compilar Solução.
Etapa 2.b: Copiar e colar o código de exemplo
Cole esse código em seu arquivo Program.cs.
Em seguida, você deve editar as cadeias de caracteres para nome do servidor, senha e assim por diante. Você pode encontrar essas cadeias de caracteres no método chamado GetSqlConnectionString.
Observação
A cadeia de conexão para o nome do servidor é voltada para o Banco de Dados SQL do Azure, porque inclui o prefixo de quatro caracteres de tcp:. Mas você pode ajustar a cadeia de caracteres do servidor para se conectar ao seu Microsoft 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);
succeeded = false;
break;
}
catch (TestSqlException sqlExc) {
if (TransientErrorNumbers.Contains(sqlExc.Number))
{
Console.WriteLine("{0}: transient occurred. (TESTING.)", sqlExc.Number);
continue;
}
Console.WriteLine(sqlExc);
succeeded = false;
break;
}
catch (Exception e)
{
Console.WriteLine(e);
succeeded = false;
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>
/// You must edit the four 'my' string values.
/// </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; }
}
Passo 2.c: Executar o programa
O executável RetryAdo2.exe não introduz parâmetros. Para executar o .exe:
- Abra uma janela de consola onde compilaste o binário RetryAdo2.exe.
- Execute RetryAdo2.exe, sem parâmetros de entrada.
database_firewall_rules_table 245575913
filestream_tombstone_2073058421 2073058421
filetable_updates_2105058535 2105058535
Etapa 3: Maneiras de testar a sua lógica de reintento
Há várias maneiras de simular um erro transitório para testar sua lógica de repetição.
Etapa 3.a: Lançar uma exceção de teste
O exemplo de código inclui:
- Uma pequena segunda classe chamada TestSqlException, com uma propriedade chamada Number.
-
//throw new TestSqlException(4060);
, que você pode descomentar.
Se você descomentar a instrução throw e recompilar, a próxima execução de RetryAdo2.exe gerará algo semelhante ao seguinte.
[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\]
>>
Etapa 3.b: Teste novamente com um erro persistente
Para provar que o código lida com erros persistentes corretamente, execute novamente o teste anterior, exceto não use o número de um erro transitório real como 4060. Em vez disso, use o número absurdo 7654321. O programa deve tratar isso como um erro persistente e deve ignorar qualquer nova tentativa.
Etapa 3.c: Desconectar da rede
- Desconecte o computador cliente da rede.
- Para um ambiente de trabalho, desligue o cabo de rede.
- Para um laptop, pressione a combinação de teclas de função para desligar o adaptador de rede.
- Inicie RetryAdo2.exee aguarde até que o console exiba o primeiro erro transitório, provavelmente 11001.
- Reconecte-se à rede, enquanto RetryAdo2.exe continua a ser executado.
- Veja o console relatar o sucesso em uma nova tentativa subsequente.
Etapa 3.d: Escrever temporariamente incorretamente o nome do servidor
- Adicione temporariamente 40615 como outro número de erro a TransientErrorNumbers e recompile.
- Defina um ponto de interrupção na linha:
new QC.SqlConnectionStringBuilder()
. - Use o recurso Editar e Continuar para escrever propositalmente incorretamente o nome do servidor, algumas linhas abaixo.
- Deixe o programa ser executado e volte ao seu ponto de interrupção.
- O erro 40615 ocorre.
- Corrija o erro ortográfico.
- Deixe o programa ser executado e concluído com êxito.
- Remova 40615 e recompile.
Próximos passos
Para explorar outras práticas recomendadas e diretrizes de design, visite Conectando-se ao Banco de Dados SQL: Links, Práticas Recomendadas e Diretrizes de Design