¿Le resultó útil esta página?
Sus comentarios sobre este contenido son muy importantes. Háganos saber su opinión.
¿Tiene comentarios adicionales?
Caracteres restantes: 1500
MSDN Library

Procedimiento: a Base de datos SQL de Azure con ADO.NET

Actualizado: julio de 2015

Las clases ADO.NET que usa para conectarse al Microsoft SQL Server local también se puede conectar a Base de datos SQL de Azure. Sin embargo, las clases ADO.NET por sí mismas no son tan sólidas ni confiables como es necesario cuando se conectan a Base de datos SQL de Azure a través de Internet.

Este tema brinda un ejemplo de código C# que muestra alguna lógica de reintento personalizado. La lógica de reintento está diseñada para procesar correctamente errores transitorios que tienden a desaparecer si el programa espera unos segundos y reintenta.

TipSugerencia
Una alternativa a la lógica de detección y la lógica de reintento es usar Enterprise Library 6 (EntLib60). EntLib60 tiene clases como ReliableSqlConnection, RetryPolicy y SqlDatabaseTransientErrorDetectionStrategy. Para obtener una comparación en el ejemplo de código en este tema, vea el ejemplo de código en Procedimiento: Conectarse a Base de datos SQL de Microsoft Azure mediante ADO.NET con Enterprise Library.

Las aplicaciones cliente que se conectan a Base de datos SQL de Azure a través de Internet se enfrentan a desafíos que requieren mayor potencia y confiabilidad. En algunas ocasiones, Internet presenta breves interrupciones que pueden dificultar una conexión con la base de datos. Además, Base de datos SQL de Azure continuamente supervisa distintas cargas de recursos y normalmente debe disminuir o reequilibrar esas cargas, lo que genera errores en el comando de conexión o consulta durante un breve periodo. De manera ocasional, el cliente podría encontrar un error durante un breve reequilibrio de carga. El cliente debe saber cómo realizar un reintento después de encontrar excepciones que se consideren como errores transitorios y que se corrijan automáticamente en cuestión de segundos.

El ejemplo de código C# de este tema contiene una lógica de reintento y detección personalizada para controlar los errores transitorios.

El ejemplo de código sigue algunas instrucciones básicas o recomendaciones que se aplican independientemente de la tecnología que se usa para interactuar con Base de datos SQL de Azure. Puede consultar las recomendaciones generales en Conexiones a Base de datos SQL de Azure: recomendaciones centrales.

El ejemplo de código C# consta de los archivos .cs cuyo contenido se pega en las secciones que siguen.

Puede compilar el ejemplo con los pasos siguientes:

  1. En Visual Studio, cree un proyecto nuevo desde la plantilla Aplicación de consola C#.

  2. Haga clic con el botón secundario en el proyecto y, a continuación, agregue el archivo .cs para el que se proporciona código fuente en este tema.

  3. En una ventana de comando cmd.exe, ejecute el programa como se muestra a continuación. También se muestra el resultado real de una ejecución.

    [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\]
    >>
    

El código fuente C# para los archivos .cs se encuentra en las siguientes secciones.

El programa de demostración está diseñado de manera tal que un error transitorio durante un intento de conexión lleve a un reintento. Sin embargo, un error transitorio durante un comando de consulta causa que el programa descarte la conexión y cree una conexión nueva, antes de reintentar el comando de consulta. Elegir este diseño queda a su criterio. El programa de demostración ilustra parte de la flexibilidad de diseño que tiene a su disposición.

La longitud del archivo Program.cs se debe principalmente a la lógica de excepción catch. Una versión abreviada de este archivo Program.cs se presenta más adelante en este tema, sin la lógica de reintento ni el control de Exception.

El método Main se encuentra en el archivo Program.cs. La pila de llamadas se ejecuta de la siguiente manera:

  1. Main llama a ConnectAndQuery.

  2. ConnectAndQuery llama a EstablishConnection.

  3. EstablishConnection llama a 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
}

El código de esta sección es una repetición abreviada del archivo Program.cs largo presentado anteriormente. Se quitó la lógica de reintento y todo el control de Exception. La versión abreviada facilita ver las llamadas de ADO.NET, sabiendo que normalmente funcionan. Normalmente no se producen errores transitorios ni se genera ninguna excepción.

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