From bb706fc51afd8c4064baac23e19c86d80b7fb7da Mon Sep 17 00:00:00 2001 From: Danimir Ljepava Date: Mon, 25 Jan 2021 20:29:23 +0100 Subject: [PATCH 1/2] Create ResilientDBApp.program --- .../ResilientDBApp.program | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 samples/tutorials/c#/Windows/SqlMIResilientCloudApp/ResilientDBApp.program diff --git a/samples/tutorials/c#/Windows/SqlMIResilientCloudApp/ResilientDBApp.program b/samples/tutorials/c#/Windows/SqlMIResilientCloudApp/ResilientDBApp.program new file mode 100644 index 00000000..7baf97e3 --- /dev/null +++ b/samples/tutorials/c#/Windows/SqlMIResilientCloudApp/ResilientDBApp.program @@ -0,0 +1,193 @@ +using System; +using System.Configuration; // use App.config file +using System.Collections.Specialized; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data.SqlClient; // use SQL client library +using CG = System.Collections.Generic; +using TD = System.Threading; +using QC = System.Data.SqlClient; + +namespace ResilientDBApp +{ + public class Program + { + static void Main(string[] args) + { + // Query executions and waits + int executions = 100; // how many executions + int sleeptime = 2; // wait time between executions (seconds) + + // Connection parameters and resiliency options + string InstanceConnectionString = "ConnMI1"; // connection string to SQL MI + bool ResilientQuery = true; // execute resilient, or non-resilient query to transient errors (retry logic true/false) + + // Query text to execute + string SQLtext = "INSERT INTO timetable (datestamp) VALUES (CURRENT_TIMESTAMP);"; + bool ReadBack = false; // read query response from SQL MI - if using SELECT statements + + try + { + for (int i = 1; i <= executions; i++) + { + Console.Write("Execution # {0} ", i); + if (!ResilientQuery) + { + Console.WriteLine ("(NON-RESILIENT)"); + InsertNonResilient(SQLtext, InstanceConnectionString, ReadBack); // execute NON-resilient DB query - NO retry logic + } else + { + Console.WriteLine("(RESILIENT)"); + InsertResilient(SQLtext, InstanceConnectionString, ReadBack); // execute resilient DB query - HAS RETRY LOGIC + } + TD.Thread.Sleep(1000 * sleeptime); // wait between the queries + } + } + catch (Exception e) + { + Console.WriteLine("Error: " + e.Message); // throw error message if connection unsuccessul + } + Console.WriteLine("\nProgram terminated. Press any key to close the window..."); + Console.ReadLine(); + } + + // Logic to execure NON-RESILIENT database query (no retry logic) + static public void InsertNonResilient(string SQLtext, string InstanceConnectionString, bool ReadBack) + { + AccessDatabase(SQLtext, InstanceConnectionString, ReadBack); + Console.WriteLine(); + } + + // Logic to execure RESILIENT database query + static public int InsertResilient(string SQLtext, string InstanceConnectionString, bool ReadBack) + { + bool succeeded = false; + int totalNumberOfTimesToTry = 4; // how many times to retry + int retryIntervalSeconds = 10; // after how many seconds + + 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 + ); + TD.Thread.Sleep(1000 * retryIntervalSeconds); + retryIntervalSeconds = Convert.ToInt32 + (retryIntervalSeconds * 1.5); + } + + AccessDatabase(SQLtext, InstanceConnectionString, ReadBack); + succeeded = true; + break; + } + + catch (QC.SqlException sqlExc) + { + if (TransientErrorNumbers.Contains + (sqlExc.Number) == true) + { + Console.WriteLine("{0}: transient error.", sqlExc.Number); + continue; + } + else + { + Console.WriteLine(sqlExc); + succeeded = false; + break; + } + } + + catch (Exception Exc) + { + Console.WriteLine(Exc); + succeeded = false; + break; + } + } + + if (succeeded == true) + { + Console.WriteLine("SUCCESS.\n"); + return 0; + } + else + { + Console.WriteLine("ERROR: Unable to access the database.\n"); + return 1; + } + } + + // Connects to the database, executes query + static public void AccessDatabase(string sqltext, string instance, bool read) + { + //throw new TestSqlException(4060); //(7654321); // Uncomment for testing. + + using (var sqlConnection = new QC.SqlConnection + (GetSqlConnectionString(instance))) + { + using (var dbCommand = sqlConnection.CreateCommand()) + { + sqlConnection.Open(); + dbCommand.CommandText = sqltext; + Console.WriteLine("Executing query: " + dbCommand.CommandText); + + // are we reading from SQL Server, or only comitting + if (read == true) // execute SQL query and read reaspons + { + var dataReader = dbCommand.ExecuteReader(); + while (dataReader.Read()) + { + Console.WriteLine(dataReader.GetString(0)); + } + } else // exectue SQL query and report rows affected + { + int rowsAffected = dbCommand.ExecuteNonQuery(); //execute query and get row count + Console.WriteLine(rowsAffected + " row(s) updated"); + } + } + } + } + + // Build connection string to the server + // ADD your SQL MI server details into App.config, + // OR manuall edit the four string values - DataSource, InitialCatalog, UserID, Password with your SQL MI connection details + static private string GetSqlConnectionString(string instance) + { + // Prepare the connection string to Azure SQL Database. + var sqlConnectionSB = new QC.SqlConnectionStringBuilder(); + + // Change these values to your values. + sqlConnectionSB.DataSource = ConfigurationManager.AppSettings.Get(instance + ".server"); // Server + sqlConnectionSB.InitialCatalog = ConfigurationManager.AppSettings.Get(instance + ".database"); // Database + sqlConnectionSB.UserID = ConfigurationManager.AppSettings.Get(instance + ".username"); + sqlConnectionSB.Password = ConfigurationManager.AppSettings.Get(instance + ".pass"); + + // Adjust these values if you like. (ADO.NET 4.5.1 or later.) + sqlConnectionSB.ConnectRetryCount = 3; + sqlConnectionSB.ConnectRetryInterval = 10; // Seconds. + + // Leave these values as they are. + sqlConnectionSB.IntegratedSecurity = false; + sqlConnectionSB.Encrypt = true; + sqlConnectionSB.ConnectTimeout = 30; + + // Test the connection string configuration online + // Console.WriteLine(sqlConnectionSB); + + return sqlConnectionSB.ToString(); + } + + // Error numbers for transient errors + static public CG.List TransientErrorNumbers = + new CG.List { 4060, 40197, 40501, 40613, 49918, 49919, 49920, 11001 }; + } + +} From c3fe6d5d1e226ddbfd28f0eff323c6bce135f08f Mon Sep 17 00:00:00 2001 From: Danimir Ljepava Date: Mon, 25 Jan 2021 21:45:35 +0100 Subject: [PATCH 2/2] Rename ResilientDBApp.program to ResilientDBApp.cs --- .../{ResilientDBApp.program => ResilientDBApp.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename samples/tutorials/c#/Windows/SqlMIResilientCloudApp/{ResilientDBApp.program => ResilientDBApp.cs} (100%) diff --git a/samples/tutorials/c#/Windows/SqlMIResilientCloudApp/ResilientDBApp.program b/samples/tutorials/c#/Windows/SqlMIResilientCloudApp/ResilientDBApp.cs similarity index 100% rename from samples/tutorials/c#/Windows/SqlMIResilientCloudApp/ResilientDBApp.program rename to samples/tutorials/c#/Windows/SqlMIResilientCloudApp/ResilientDBApp.cs