Etapa 4: Conecte-se de forma resiliente ao SQL com o ADO.NET

Descarregar ADO.NET

Este artigo apresenta um exemplo de código C# que demonstra a lógica de repetição implementada de forma manual. A lógica de repetição lida com erros temporários, ou falhas transitórias, que podem ser corrigidos se o programa esperar e tentar novamente a operação.

Importante

Para novas aplicações que utilizem o Microsoft.Data.SqlClient 3.0 e versões posteriores, dê preferência à lógica de repetição configurável do controlador, em vez de implementar um ciclo de repetição. Comece por Configurar a lógica de repetição no SqlClient.

As fontes de falhas transitórias incluem:

  • Uma breve falha da rede que suporta a Internet.
  • Um sistema cloud pode estar a equilibrar os seus recursos quando a sua consulta chega.

As classes ADO.NET para ligação ao SQL Server também podem ligar-se ao Base de Dados SQL do Azure. O seu programa cliente pode encontrar falhas transitórias. Deve recuperar-se utilizando uma política de retentativas limitada que seja adequada para a operação.

Etapa 1: Identificar erros transitórios

O seu programa deve distinguir entre erros transitórios e erros persistentes. Erros transitórios são condições de erro que podem desaparecer rapidamente, como problemas de rede transitória. Um exemplo de erro persistente é um erro ortográfico do nome da base de dados de destino. Nesse caso, o erro "Nenhuma base de dados encontrada" persiste e não desaparece rapidamente.

A lista de números de erro categorizados como falhas transitórias está disponível em Mensagens de erro para aplicações cliente de base 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 num ficheiro 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:

  1. No edição gratuita do Visual Studio Community, crie um novo projeto a partir do modelo Aplicativo de Console do C#.

    • Selecione Ficheiro>NovoProjeto>>>Modelos>Visual C#>Windows>Ambiente de Trabalho Clássico>Aplicação de Consola.

    • Nomeie o projeto RetryAdo2.

  2. Abra o painel Gerenciador de Soluções.

  3. Abra o ficheiro Program.cs.

  4. Substitua todo o conteúdo do Program.cs ficheiro pelo código do bloco de código seguinte.

  5. Selecione o menu Build>Build Solution.

Etapa 2.b: Copiar e colar o código de exemplo

Cola este código no teu Program.cs ficheiro.

Depois, edita as strings para o nome do servidor e a palavra-passe. Pode encontrar estas cadeias no método chamado GetSqlConnectionString.

Observação

A cadeia de ligação é orientada para o Base de Dados SQL do Azure, porque inclui o prefixo tcp:de quatro caracteres . Mas pode ajustar a cadeia de caracteres do servidor para estabelecer ligação à sua instância do 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; }
}

Passo 2.c: Executar o programa

O RetryAdo2.exe executável não aceita parâmetros. Para executar o .exe:

  1. Abre uma janela de consola na pasta onde compilaste o RetryAdo2.exe binário.
  2. Executa 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

Pode simular um erro transitório para testar a lógica de nova tentativa de várias formas.

Etapa 3.a: Lançar uma exceção de teste

O exemplo de código inclui:

  • Uma pequena segunda classe nomeada TestSqlException com uma propriedade chamada Number.
  • //throw new TestSqlException(4060); , que você pode descomentar.

Se descomentares a instrução `throw` e recompilares, a execução seguinte de RetryAdo2.exe produz 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 corretamente com erros persistentes, reexecute o teste anterior. Em vez de usar um número de erro transitório real como o 4060, use o número fictício 7654321. O programa trata este número como um erro persistente e ignora qualquer retentativa.

Etapa 3.c: Desconectar da rede

  1. 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.
  2. Inicia RetryAdo2.exe, e espera que a consola mostre o primeiro erro transitório, provavelmente 11001.
  3. Reconecte-se à rede, enquanto RetryAdo2.exe continua a funcionar.
  4. Veja o console relatar o sucesso em uma nova tentativa subsequente.

Etapa 3.d: Escrever temporariamente incorretamente o nome do servidor

  1. Adicionar temporariamente 40615 como outro número de erro a TransientErrorNumbers, e recompilar.
  2. Defina um ponto de interrupção na linha: new QC.SqlConnectionStringBuilder().
  3. 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.
  4. Corrija o erro ortográfico.
  5. Deixe o programa ser executado e concluído com êxito.
  6. Remova 40615 e recompile.

Próximo passo