Questa pagina è stata utile?
I suggerimenti relativi al contenuto di questa pagina sono importanti. Comunicaceli.
Altri suggerimenti?
1500 caratteri rimanenti
Esporta (0) Stampa
Espandi tutto

Procedura: Connessione al database SQL di Azure tramite ADO.NET

Aggiornamento: luglio 2015

Le classi ADO.NET usate per connettersi a Microsoft SQL Server locale possono inoltre connettersi al database SQL di Azure. Tuttavia, le classi ADO.NET, per quanto necessarie, non sono altrettanto efficaci e affidabili durante la connessione al database SQL di Azure tramite Internet.

In questo argomento viene fornito un esempio di codice C# che illustra una logica di ripetizione dei tentativi personalizzata. La logica di ripetizione dei tentativi è progettata per elaborare correttamente gli errori temporanei che tendono a risolversi se il programma esegue il nuovo tentativo dopo alcuni secondi.

TipSuggerimento
Come alternativa alla logica di rilevamento e di ripetizione dei tentativi è possibile usare Enterprise Library 6 (EntLib60). EntLib60 contiene classi come ReliableSqlConnection, RetryPolicy e SqlDatabaseTransientErrorDetectionStrategy. Per un confronto con l'esempio di codice del presente argomento, vedere l'esempio di codice in Procedura: Connessione al database SQL di Azure usando ADO.NET con Enterprise Library.

Le applicazioni client che si connettono al database SQL di Azure tramite Internet affrontano problematiche che richiedono solidità e affidabilità. La rete Internet è soggetta occasionalmente a brevi interruzioni a singhiozzo in grado di terminare una connessione al database. Inoltre, il database SQL di Azure monitora costantemente i vari carichi delle risorse e deve a volte limitare o ribilanciare tali carichi, che possono causare errori di connessione o dei comandi di query durante un breve intervallo di tempo. In alcuni casi il client può riscontrare un errore durante un breve ribilanciamento del carico. Il client deve essere in grado di eseguire un nuovo tentativo dopo aver riscontrato eccezioni che sono considerate errori temporanei e che potrebbero essere risolte automaticamente in pochi secondi.

L'esempio di codice C# del presente argomento contiene la logica di rilevamento e di ripetizione dei tentativi personalizzata per gestire gli errori temporanei.

Nell'esempio di codice vengono seguite alcune indicazioni o linee guida di base applicabili indipendentemente dalla tecnologia usata per interagire con il database SQL di Azure. È possibile consultare le indicazioni generali nell'articolo relativo alle indicazioni generali per la connessione al database SQL di Azure.

L'esempio di codice C# è costituito dai file .cs il cui contenuto viene incollato nelle sezioni che seguono.

È possibile compilare l'esempio di codice effettuando i seguenti passaggi:

  1. In Visual Studio creare un nuovo progetto a partire dal modello di applicazione console C#.

  2. Fare clic con il pulsante destro del mouse sul progetto e aggiungere il file .cs di cui viene fornito il codice sorgente in questo argomento.

  3. In una finestra di comando cmd.exe eseguire il programma come indicato di seguito. Viene inoltre visualizzato l'output effettivo di un'esecuzione.

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

Il codice sorgente C# per i file .cs è riportato nelle sezioni seguenti.

Il programma demo è progettato in modo tale che un errore temporaneo durante un tentativo di connessione determina un nuovo tentativo. Tuttavia, un errore temporaneo durante un comando di query determina l'annullamento della connessione e la creazione di una connessione prima che venga effettuato un nuovo tentativo di esecuzione del comando di query. Questa scelta di progettazione non è né consigliata né sconsigliata. Il programma demo illustra in parte la flessibilità di progettazione disponibile.

La lunghezza del file Program.cs è dovuta principalmente alla logica delle eccezioni catch. Più avanti in questo argomento viene illustrata una versione più breve del file Program.cs, da cui la logica di ripetizione dei tentativi e la gestione delle Exception sono state interamente rimosse.

Il metodo Main si trova in Program.cs. Lo stack di chiamate viene eseguito come segue:

  1. Main chiama ConnectAndQuery.

  2. ConnectAndQuery chiama EstablishConnection.

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

Il codice contenuto in questa sezione è una ripetizione abbreviata del file Program.cs completo presentato in precedenza. La logica di ripetizione dei tentativi e la gestione delle Exception sono state interamente rimosse. La versione breve semplifica la visualizzazione delle chiamate ADO.NET, sapendo che queste in genere funzionano. Di norma non si verificano errori temporanei e non vengono generate eccezioni.

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
}

Microsoft sta conducendo un sondaggio in linea per comprendere l'opinione degli utenti in merito al sito Web di MSDN. Se si sceglie di partecipare, quando si lascia il sito Web di MSDN verrà visualizzato il sondaggio in linea.

Si desidera partecipare?
Mostra:
© 2015 Microsoft