'ADO.NET - Resilient Connection to Oracle Database using OracleClient
We have a legacy C# Windows Service application (.Net Framework 4.8) that performs some statistical analysis which usually takes hours to complete as the underlying database has millions of rows of historical data.
It works fine if there is no underlying network interruption. However, we recently started using a database which is only accessible over the VPN. Now if there is any VPN connection issue, the analysis stops.
Is there any way to recover from these transient faults silently and gracefully and continue the work?
For the SqlClient I have found a sample code at https://docs.microsoft.com/en-us/sql/connect/ado-net/step-4-connect-resiliently-sql-ado-net?view=sql-server-ver15
The sample code from the link is shown below (just in case if the link dies).
using System; // C#
using CG = System.Collections.Generic;
using QC = Microsoft.Data.SqlClient;
using TD = System.Threading;
namespace RetryAdo2
{
public class Program
{
static public int Main(string[] args)
{
bool succeeded = false;
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
);
TD.Thread.Sleep(1000 * retryIntervalSeconds);
retryIntervalSeconds = Convert.ToInt32
(retryIntervalSeconds * 1.5);
}
AccessDatabase();
succeeded = true;
break;
}
catch (QC.SqlException sqlExc)
{
if (TransientErrorNumbers.Contains
(sqlExc.Number) == true)
{
Console.WriteLine("{0}: transient occurred.", sqlExc.Number);
continue;
}
else
{
Console.WriteLine(sqlExc);
succeeded = false;
break;
}
}
catch (TestSqlException sqlExc)
{
if (TransientErrorNumbers.Contains
(sqlExc.Number) == true)
{
Console.WriteLine("{0}: transient occurred. (TESTING.)", sqlExc.Number);
continue;
}
else
{
Console.WriteLine(sqlExc);
succeeded = false;
break;
}
}
catch (Exception Exc)
{
Console.WriteLine(Exc);
succeeded = false;
break;
}
}
if (succeeded == true)
{
return 0;
}
else
{
Console.WriteLine("ERROR: Unable to access the database!");
return 1;
}
}
/// <summary>
/// Connects to the database, reads,
/// prints results to the console.
/// </summary>
static public void AccessDatabase()
{
//throw new TestSqlException(4060); //(7654321); // Uncomment for testing.
using (var sqlConnection = new QC.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>
/// You must edit the four 'my' string values.
/// </summary>
/// <returns>An ADO.NET connection string.</returns>
static private string GetSqlConnectionString()
{
// Prepare the connection string to Azure SQL Database.
var sqlConnectionSB = new QC.SqlConnectionStringBuilder();
// Change these values to your values.
sqlConnectionSB.DataSource = "tcp:myazuresqldbserver.database.windows.net,1433"; //["Server"]
sqlConnectionSB.InitialCatalog = "MyDatabase"; //["Database"]
sqlConnectionSB.UserID = "MyLogin"; // "@yourservername" as suffix sometimes.
sqlConnectionSB.Password = "MyPassword";
sqlConnectionSB.IntegratedSecurity = false;
// 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;
return sqlConnectionSB.ToString();
}
static public CG.List<int> TransientErrorNumbers =
new CG.List<int> { 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)
{ this.Number = testErrorNumber; }
internal int Number
{ get; set; }
}
}
However, I couldn't find any helpful material for the OracleClient. Any ideas, please?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
