Esta página foi útil?
Seus comentários sobre este conteúdo são importantes. Queremos saber sua opinião.
Comentários adicionais?
1500 caracteres restantes
Exportar (0) Imprimir
Expandir Tudo

Como: Conectar-se ao Banco de Dados SQL do Azure usando o ADO.NET

Atualizado: julho de 2015

As classes do ADO.NET que você usa 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 são tão robustas e confiáveis o necessário ao se conectar ao Banco de Dados SQL do Azure pela Internet.

Este tópico fornece um exemplo de código do C# que demonstra alguma lógica de repetição personalizada. A lógica de repetição foi projetada para processar normalmente erros transitórios que tendem a desaparecer se o programa esperar alguns segundos e tentar novamente.

TipDica
Uma alternativa para lógica de detecção e repetição personalizada é usar o Enterprise Library 6 (EntLib60). O EntLib60 possui classes como ReliableSqlConnection, RetryPolicy e SqlDatabaseTransientErrorDetectionStrategy. Para obter uma comparação para o exemplo de código no tópico presente, consulte o exemplo de código em Como: Conectar-se ao Banco de dados SQL do Azure usando o ADO.NET com a Biblioteca Corporativa.

Os aplicativos cliente que se conectam ao Banco de Dados SQL do Azure pela Internet enfrentam desafios que exigem mais eficiência e confiabilidade. Ocasionalmente, a Internet tem dificuldades que podem encerrar uma conexão do banco de dados. Além disso, o Banco de Dados SQL do Azure monitora constantemente várias cargas de recursos e, ocasionalmente, deve reduzir ou reequilibrar as cargas, o que pode causar falhas no comando de consulta ou na conexão durante um breve período de tempo. Às vezes, o cliente pode encontrar uma falha durante um novo balanceamento de carga breve. O cliente deve saber como tentar novamente depois de encontrar exceções que são consideradas falhas transitórias e que devem ser corrigidas automaticamente em segundos.

O exemplo de código do C# neste tópico contém lógica de detecção e repetição personalizada para manipular erros transitórios.

O exemplo de código segue algumas diretrizes básicas ou recomendações que se aplicam independentemente de qual tecnologia você usa para interagir com o Banco de Dados SQL do Azure. Você pode consultar as recomendações gerais em Conexões ao Banco de Dados SQL do Azure: recomendações centrais.

O exemplo de código do C# consiste de arquivos .cs, cujo conteúdo é colado nas seções a seguir.

Você pode compilar o exemplo com as seguintes etapas:

  1. No Visual Studio, crie um novo projeto do modelo de Aplicativo de Console do C#.

  2. Clique com o botão direito do mouse no projeto e adicione o .cs para o qual o código-fonte é fornecido neste tópico.

  3. Em uma janela de comando cmd.exe, execute o programa, como mostrado a seguir. Também é mostrada a saída real de uma execução.

    [C:\MyVS\ConsoleApplication1\ConsoleApplication1\bin\Debug\]
    >> ConsoleApplication1.exe
    database_firewall_rules_table   245575913
    filestream_tombstone_2073058421 2073058421
    filetable_updates_2105058535    2105058535
    
    [C:\MyVS\ConsoleApplication1\ConsoleApplication1\bin\Debug\]
    >>
    

O código-fonte do C# dos arquivos .cs é apresentado nas seções a seguir.

O programa de demonstração foi projetado para que um erro temporário durante a tentativa de conexão leve a uma repetição. Mas um erro temporário durante o comando de consulta faz com que o programa descarte a conexão e crie uma nova conexão, antes de repetir o comando de consulta. Não recomendamos ou desaconselhamos essa opção de design. O programa de demonstração ilustra um pouco a flexibilidade de design que está disponível para você.

O comprimento do arquivo Program.cs se deve principalmente à lógica de exceção de captura. Uma versão mais curta desse arquivo Program.cs será apresentada mais adiante neste tópico, com toda a lógica de repetição e tratamento de Exception removidos.

O método Main está em Program.cs. A pilha de chamadas é executada como a seguir:

  1. Main chamadas ConnectAndQuery.

  2. ConnectAndQuery chamadas EstablishConnection.

  3. EstablishConnection chamadas IssueQueryCommand.

using     System;  // C#, pure ADO.NET, no Enterprise Library.
using X = System.Text;
using D = System.Data;
using C = System.Data.SqlClient;
using T = System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        // Fields, shared among methods.
        C.SqlConnection sqlConnection;
        C.SqlConnectionStringBuilder scsBuilder;

        static void Main(string[] args)
        {
            new Program().ConnectAndQuery();
        }

        /// <summary>
        /// Prepares values for a connection. Then inside a loop, it calls a method
        /// that opens a connection. The called method calls yet another method
        /// that issues a query.
        /// The loop reiterates only if a transient error is encountered.
        /// </summary>
        void ConnectAndQuery()
        {
            int connectionTimeoutSeconds = 30;  // Default of 15 seconds is too short over the Internet, sometimes.
            int maxCountTriesConnectAndQuery = 3;  // You can adjust the various retry count values.
            int secondsBetweenRetries = 4;  // Simple retry strategy.

            // [A.1] Prepare the connection string to Azure SQL Database.
            this.scsBuilder = new C.SqlConnectionStringBuilder();
            // Change these values to your values.
            this.scsBuilder["Server"] = "tcp:myazuresqldbserver.database.windows.net,1433";
            this.scsBuilder["User ID"] = "MyLogin";  // @yourservername suffix sometimes.
            this.scsBuilder["Password"] = "MyPassword";
            this.scsBuilder["Database"] = "MyDatabase";
            // Leave these values as they are.
            this.scsBuilder["Trusted_Connection"] = false;
            this.scsBuilder["Integrated Security"] = false;
            this.scsBuilder["Encrypt"] = true;
            this.scsBuilder["Connection Timeout"] = connectionTimeoutSeconds;

            //-------------------------------------------------------
            // Preparations are complete.

            for (int cc = 1; cc <= maxCountTriesConnectAndQuery; cc++)
            {
                try
                {
                    // [A.2] Connect, which proceeds to issue a query command.
                    this.EstablishConnection();

                    // [A.3] All has gone well, so let the program end.
                    break;
                }
                catch (C.SqlException sqlExc)
                {
                    bool isTransientError;

                    // [A.4] Check whether sqlExc.Number is on the whitelist of transients.
                    isTransientError = Custom_SqlDatabaseTransientErrorDetectionStrategy
                        .IsTransientStatic(sqlExc);

                    if (isTransientError == false)  // Is a persistent error...
                    {
                        Console.WriteLine();
                        Console.WriteLine("Persistent error suffered, SqlException.Number=={0}.  Will terminate.",
                            sqlExc.Number);
                        Console.WriteLine(sqlExc.ToString());

                        // [A.5] Either the connection attempt or the query command attempt suffered a persistent SqlException.
                        // Break the loop, let the hopeless program end.
                        break;
                    }

                    // [A.6] The SqlException identified a transient error from an attempt to issue a query command.
                    // So let this method reloop and try again. However, we recommend that the new query
                    // attempt should start at the beginning and establish a new connection.
                    Console.WriteLine();
                    Console.WriteLine("Transient error encountered.  SqlException.Number=={0}.  Program might retry by itself.", sqlExc.Number);
                    Console.WriteLine("{0} = Attempts so far. Might retry.", cc);
                    Console.WriteLine(sqlExc.Message);
                }
                catch (Exception exc)
                {
                    Console.WriteLine();
                    Console.WriteLine("Unexpected exception type caught in Main. Will terminate.");

                    // [A.7] The program must end, so re-throw the unrecognized error.
                    throw exc;
                }

                // [A.8] Throw an application exception if transient SqlExceptions caused us
                // to exceed our self-imposed maximum count of retries.
                if (cc > maxCountTriesConnectAndQuery)
                {
                    Console.WriteLine();
                    string mesg = String.Format(
                        "Transient errors suffered in too many retries ({0}). Will terminate.",
                        cc - 1);
                    Console.WriteLine(mesg);

                    // [A.9] To end the program, throw a new exception of a different type.
                    ApplicationException appExc = new ApplicationException(mesg);
                    throw appExc;
                }
                // Else, can retry.

                // A very simple retry strategy, a brief pause before looping.
                T.Thread.Sleep(1000 * secondsBetweenRetries);
            } // for cc
            return;
        } // method ConnectAndQuery


        /// <summary>
        /// Open a connection, then call a method that issues a query.
        /// </summary>
        void EstablishConnection()
        {
            try
            {
                // [B.1] The 'using' statement will .Dispose() the connection.
                // If you are working with a connection pool, you might want instead
                // to merely .Close() the connection.
                using (this.sqlConnection = new C.SqlConnection(this.scsBuilder.ToString()))
                {
                    // [B.2] Open a connection.
                    sqlConnection.Open();
                    // [B.3]
                    this.IssueQueryCommand();
                }
            }
            catch (Exception exc)
            {
                // [B.4] This re-throw means we discard the connection whenever
                // any error occurs during query command, even for a transient error.
                throw exc;  // [B.5] Let caller assess any exception, SqlException or any kind.
            }
            return;
        } // method EstablishConnection


        /// <summary>
        /// Issue a query, then write the result rows to the console.
        /// </summary>
        void IssueQueryCommand()
        {
            D.IDataReader dReader = null;
            D.IDbCommand dbCommand = null;
            X.StringBuilder sBuilder = new X.StringBuilder(512);

            try
            {
                // [C.1] Use the connection to create a query command.
                using (dbCommand = this.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;";

                    // [C.2] Issue the query command through the connection.
                    using (dReader = dbCommand.ExecuteReader())
                    {
                        // [C.3] Loop through all returned rows, writing the data to the console.
                        while (dReader.Read())
                        {
                            sBuilder.Length = 0;
                            sBuilder.Append(dReader.GetString(0));
                            sBuilder.Append("\t");
                            sBuilder.Append(dReader.GetString(1));

                            Console.WriteLine(sBuilder.ToString());
                        }
                    }
                }
            }
            catch (Exception exc)
            {
                throw exc; // Let caller assess any exception.
            }
            return;
        } // method IssueQueryCommand
    } // class Program
}

using     System;
using G = System.Collections.Generic;
using C = System.Data.SqlClient;

namespace ConsoleApplication1
{
    /// <summary>
    /// A custom alternative to class SqlDatabaeTransientErrorDetectionStrategy.
    /// </summary>
    public class Custom_SqlDatabaseTransientErrorDetectionStrategy
    {
        static private G.List<int> M_listTransientErrorNumbers;


        /// <summary>
        /// This method happens to match ITransientErrorDetectionStrategy of EntLib60.
        /// </summary>
        public bool IsTransient(Exception exc)
        {
            return IsTransientStatic(exc);
        }


        /// <summary>
        /// For general use beyond formal Enterprise Library classes.
        /// </summary>
        static public bool IsTransientStatic(Exception exc)
        {
            bool returnBool = false;  // Assume is a persistent error.
            C.SqlException sqlExc;

            if (exc is C.SqlException)
            {
                sqlExc = exc as C.SqlException;
                if (M_listTransientErrorNumbers.Contains(sqlExc.Number) == true)
                {
                    returnBool = true;  // Error is transient, not persistent.
                }
            }
            return returnBool;
        }


        /// <summary>
        /// Lists the SqlException.Number values that are considered
        /// to indicate transient errors.
        /// </summary>
        static Custom_SqlDatabaseTransientErrorDetectionStrategy()
        {
            int[] arrayOfTransientErrorNumbers =
                {4060, 10928, 10929, 40197, 40501, 40613
};

            M_listTransientErrorNumbers = new G.List<int>(arrayOfTransientErrorNumbers);
        }
    } // class Custom_SqlDatabaseTransientErrorDetectionStrategy
}

O código desta seção é uma repetição abreviada do arquivo Program.cs longo apresentado anteriormente. Toda a lógica de repetição e todo o tratamento Exception foi removido. A versão curta facilita ver as chamadas do ADO.NET, sabendo que elas funcionam normalmente. Geralmente, não ocorrem erros transitórios e nenhuma exceção é lançada.

using     System;  // C#, pure ADO.NET, no retry logic, no Exception handling.
using X = System.Text;
using D = System.Data;
using C = System.Data.SqlClient;

namespace ConsoleApplication1_dn864744
{
    class Program
    {
        C.SqlConnection sqlConnection;
        C.SqlConnectionStringBuilder scsBuilder;

        static void Main(string[] args)
        {
            new Program().ConnectAndQuery();
        }

        void ConnectAndQuery()
        {
            this.scsBuilder = new C.SqlConnectionStringBuilder();
            // Change these values to your values.
            this.scsBuilder["Server"] = "tcp:myazuresqldbserver.database.windows.net,1433";
            this.scsBuilder["User ID"] = "MyLogin";
            this.scsBuilder["Password"] = "MyPassword";
            this.scsBuilder["Database"] = "MyDatabase";
            this.scsBuilder["Trusted_Connection"] = false;
            this.scsBuilder["Integrated Security"] = false;
            this.scsBuilder["Encrypt"] = true;
            this.scsBuilder["Connection Timeout"] = 30;

            this.EstablishConnection();
        } // method ConnectAndQuery

        void EstablishConnection()
        {
            using (this.sqlConnection = new C.SqlConnection(this.scsBuilder.ToString()))
            {
                sqlConnection.Open();
                this.IssueQueryCommand();
            }
        } // method EstablishConnection

        void IssueQueryCommand()
        {
            D.IDataReader dReader = null;
            D.IDbCommand dbCommand = null;
            X.StringBuilder sBuilder = new X.StringBuilder(512);

            using (dbCommand = this.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;";

                using (dReader = dbCommand.ExecuteReader())
                {
                    while (dReader.Read())
                    {
                        sBuilder.Length = 0;
                        sBuilder.Append(dReader.GetString(0));
                        sBuilder.Append("\t");
                        sBuilder.Append(dReader.GetString(1));
                        Console.WriteLine(sBuilder.ToString());
                    }
                }
            }
        } // method IssueQueryCommand
    } // class Program
}

Mostrar:
© 2015 Microsoft