diff --git a/src/bnhtrade.Core/Data/Database/Connection.cs b/src/bnhtrade.Core/Data/Database/Connection.cs
index bfdc612..c171de3 100644
--- a/src/bnhtrade.Core/Data/Database/Connection.cs
+++ b/src/bnhtrade.Core/Data/Database/Connection.cs
@@ -1,92 +1,78 @@
-using System;
+using FikaAmazonAPI.AmazonSpApiSDK.Models.Services;
+using System;
using System.Configuration;
namespace bnhtrade.Core.Data.Database
{
+ /// this class needs a sort out. Ideally it shoud be called what it is, a connection string builder, and
+ /// it should expose a method to serve the connection string--rather than class inheritance and using a property setter
+ /// something to do once there aren't so many open git branches
+ ///
public class Connection
{
- //protected readonly string SqlConnectionString;
- private Model.Credentials.bnhtradeDB _dbCredentials;
+ private string _server;
+ private string _user;
+ private string _userPassword;
+ private string _database = "e2A";
+ private bool _persistSecurityInfo = true;
+ private bool _multipleActiveResultSets = true;
+ private bool _encrypt = true;
+ private uint _connectRetryInterval;
+ private uint _connectRetryCount;
+ private uint _connectTimeout;
- protected string SqlConnectionString
- {
- get { return _dbCredentials.ConnectionString; }
- }
+ internal string SqlConnectionString { get; private set; }
- public Connection()
+ ///
+ ///
+ ///
+ /// Retry interval in seconds, must be 5-60
+ /// Timeout length in seconds (0 is indefinitely)
+ ///
+ ///
+ public Connection(uint connectRetryInterval = 10, uint connectRetryCount = 6, uint connectionTimeout = 60)
{
- var config = new Config().GetConfiguration();
+ if (connectRetryInterval < 5 || connectRetryInterval > 60)
+ {
+ // these are limits set by the sql server
+ throw new ArgumentOutOfRangeException("ConnectRetryInterval must be from 5 to 60 seconds");
+ }
+ _connectRetryInterval = connectRetryInterval;
+ _connectRetryCount = connectRetryCount;
+ _connectTimeout = connectionTimeout;
- // attempt to retrive credentials from app.local.config
+ // retrive credentials from app.local.config
+ var config = new Config().GetConfiguration();
try
{
- string dataSource = config.AppSettings.Settings["DbDataSource"].Value;
- string userId = config.AppSettings.Settings["DbUserId"].Value;
- string pass = config.AppSettings.Settings["DbUserPassword"].Value;
+ _server = config.AppSettings.Settings["DbDataSource"].Value;
+ _user = config.AppSettings.Settings["DbUserId"].Value;
+ _userPassword = config.AppSettings.Settings["DbUserPassword"].Value;
// check
- if (string.IsNullOrEmpty(dataSource))
+ if (string.IsNullOrEmpty(_server))
{
throw new ArgumentException("Could not retrive 'DbDataSource' from config file");
}
- else if (string.IsNullOrEmpty(userId))
+ else if (string.IsNullOrEmpty(_user))
{
throw new ArgumentException("Could not retrive 'DbUserId' from config file");
}
- else if (string.IsNullOrEmpty(pass))
+ else if (string.IsNullOrEmpty(_userPassword))
{
throw new ArgumentException("Could not retrive 'DbUserPassword' from config file");
}
-
- var dbCredentials = new bnhtrade.Core.Model.Credentials.bnhtradeDB(dataSource, userId, pass);
- this._dbCredentials = dbCredentials;
}
catch (Exception ex)
{
throw new Exception("Unable to retirve DB credentials: " + ex.Message);
}
- }
- private void ConnectionOld()
- {
- // attempt to retrive credentials from app.local.config
- try
- {
- string dataSource = ConfigurationManager.AppSettings["DbDataSource"];
- string userId = ConfigurationManager.AppSettings["DbUserId"];
- string pass = ConfigurationManager.AppSettings["DbUserPassword"];
-
- // check
- if (string.IsNullOrEmpty(dataSource))
- {
- throw new ArgumentException("Could not retrive 'DbDataSource' from config file");
- }
- else if (string.IsNullOrEmpty(userId))
- {
- throw new ArgumentException("Could not retrive 'DbUserId' from config file");
- }
- else if (string.IsNullOrEmpty(pass))
- {
- throw new ArgumentException("Could not retrive 'DbUserPassword' from config file");
- }
-
- var dbCredentials = new bnhtrade.Core.Model.Credentials.bnhtradeDB(dataSource, userId, pass);
- this._dbCredentials = dbCredentials;
- }
- catch(Exception ex)
- {
- throw new Exception("Unable to retirve DB credentials: " + ex.Message);
- }
- }
-
- public Connection(Model.Credentials.bnhtradeDB dbCredentials)
- {
- // setup sql parameters
- if (dbCredentials == null)
- {
- throw new Exception("DB credentials object is null");
- }
- this._dbCredentials = dbCredentials;
+ // build connection string
+ SqlConnectionString = "Server=" + _server + ";Database=" + _database + ";PersistSecurityInfo=" + _persistSecurityInfo.ToString()
+ + ";User=" + _user + ";Password=" + _userPassword + ";MultipleActiveResultSets=" + _multipleActiveResultSets.ToString()
+ + ";ConnectRetryInterval=" + _connectRetryInterval + ";ConnectRetryCount=" + _connectRetryCount + ";Timeout=" + _connectTimeout
+ + ";Encrypt=" + _encrypt.ToString() +";";
}
}
}
diff --git a/src/bnhtrade.Core/Data/Database/SKU/Price/ReadParameter.cs b/src/bnhtrade.Core/Data/Database/SKU/Price/ReadParameter.cs
index 7839832..dc9b15f 100644
--- a/src/bnhtrade.Core/Data/Database/SKU/Price/ReadParameter.cs
+++ b/src/bnhtrade.Core/Data/Database/SKU/Price/ReadParameter.cs
@@ -10,11 +10,6 @@ namespace bnhtrade.Core.Data.Database.Sku.Price
{
public class ReadParameter : Connection
{
- public ReadParameter() : base()
- {
-
- }
-
public List Execute()
{
string stringSql = @"
diff --git a/src/bnhtrade.Core/Data/Database/ServerPing.cs b/src/bnhtrade.Core/Data/Database/ServerPing.cs
new file mode 100644
index 0000000..3eb6a51
--- /dev/null
+++ b/src/bnhtrade.Core/Data/Database/ServerPing.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Data.SqlClient;
+
+namespace bnhtrade.Core.Data.Database
+{
+ internal class ServerPing
+ {
+ internal ServerPing()
+ {
+ }
+
+ internal Logic.Log.LogEvent log = new Logic.Log.LogEvent();
+
+ ///
+ /// Polls SQL Server until it comes online, max attempts are reached, or the cancellation token is triggered.
+ ///
+ /// Seconds each connection attempt waits before failing.
+ /// Maximum number of poll attempts. 0 = retry indefinitely.
+ /// Token to cancel the polling loop.
+ /// True when the server responds, false if max attempts reached or cancelled.
+ internal async Task WaitForServerAsync(uint timeout = 10, uint maxAttempts = 6, CancellationToken cancellationToken = default)
+ {
+ var connection = new Connection(connectionTimeout: timeout);
+
+ int attempt = 0;
+ DateTime firstAttempt = DateTime.UtcNow;
+ int lineWidth = Console.WindowWidth - 1;
+ Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Starting to poll SQL Server for availability...");
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ attempt++;
+ bool isFinalAttempt = maxAttempts > 0 && attempt >= maxAttempts;
+
+ Console.WriteLine($"\r[{DateTime.Now:HH:mm:ss}] Attempt {attempt} of {maxAttempts} ");
+
+ try
+ {
+ using SqlConnection conn = new SqlConnection(connection.SqlConnectionString);
+ if (attempt == 1)
+ firstAttempt = DateTime.UtcNow;
+ await conn.OpenAsync(cancellationToken);
+ Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] SQL Server is online, attempt {attempt} succeeded!");
+ return true;
+ }
+ catch (OperationCanceledException)
+ {
+ Console.WriteLine($"\n[{DateTime.Now:HH:mm:ss}] Polling cancelled.");
+ return false;
+ }
+ catch (SqlException ex)
+ {
+ if (isFinalAttempt)
+ {
+ Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] SQL Server not available (error {ex.Number}): {ex.Message}");
+ }
+ }
+
+ if (isFinalAttempt)
+ {
+ Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Max attempts ({maxAttempts}) reached, could not connect to SQL Server.");
+ return false;
+ }
+
+ // countdown overwrites the same line as the attempt message
+ int retrySeconds = (firstAttempt.AddSeconds(timeout * attempt) - DateTime.UtcNow).Seconds ;
+ for (int i = retrySeconds; i > 0; i--)
+ {
+ Console.Write($"\r[--------] Retrying in {i}s...");
+ try
+ {
+ await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
+ }
+ catch (OperationCanceledException)
+ {
+ Console.WriteLine($"\n[{DateTime.Now:HH:mm:ss}] Polling cancelled.");
+ return false;
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/bnhtrade.Core/Data/Database/UnitOfWork/Connection.cs b/src/bnhtrade.Core/Data/Database/UnitOfWork/Connection.cs
deleted file mode 100644
index e4b298c..0000000
--- a/src/bnhtrade.Core/Data/Database/UnitOfWork/Connection.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.Data.SqlClient;
-using System.Configuration;
-
-namespace bnhtrade.Core.Data.Database.UnitOfWork
-{
- public class Connection
- {
- //protected readonly string SqlConnectionString;
- private Model.Credentials.bnhtradeDB _dbCredentials;
-
- protected string SqlConnectionString
- {
- get { return _dbCredentials.ConnectionString; }
- }
-
- public Connection()
- {
- var config = new Config().GetConfiguration();
-
- // attempt to retrive credentials from app.local.config
- try
- {
- string dataSource = config.AppSettings.Settings["DbDataSource"].Value;
- string userId = config.AppSettings.Settings["DbUserId"].Value;
- string pass = config.AppSettings.Settings["DbUserPassword"].Value;
-
- // check
- if (string.IsNullOrEmpty(dataSource))
- {
- throw new ArgumentException("Could not retrive 'DbDataSource' from config file");
- }
- else if (string.IsNullOrEmpty(userId))
- {
- throw new ArgumentException("Could not retrive 'DbUserId' from config file");
- }
- else if (string.IsNullOrEmpty(pass))
- {
- throw new ArgumentException("Could not retrive 'DbUserPassword' from config file");
- }
-
- var dbCredentials = new bnhtrade.Core.Model.Credentials.bnhtradeDB(dataSource, userId, pass);
- this._dbCredentials = dbCredentials;
- }
- catch (Exception ex)
- {
- throw new Exception("Unable to retirve DB credentials: " + ex.Message);
- }
- }
- }
-}
diff --git a/src/bnhtrade.Core/Logic/Stock/SkuTransactionTypeCrud.cs b/src/bnhtrade.Core/Logic/Stock/SkuTransactionTypeCrud.cs
index 163afa4..2f7510c 100644
--- a/src/bnhtrade.Core/Logic/Stock/SkuTransactionTypeCrud.cs
+++ b/src/bnhtrade.Core/Logic/Stock/SkuTransactionTypeCrud.cs
@@ -9,7 +9,7 @@ using System.Transactions;
namespace bnhtrade.Core.Logic.Stock
{
- public class SkuTransactionTypeCrud : Connection // this inheritance can be removed when old code is removed below
+ public class SkuTransactionTypeCrud
{
private Data.Database.Stock.ReadSkuTransactionType dbRead;
diff --git a/src/bnhtrade.Core/Logic/Utilities/NightlyRoutine.cs b/src/bnhtrade.Core/Logic/Utilities/NightlyRoutine.cs
index a5a843e..0601868 100644
--- a/src/bnhtrade.Core/Logic/Utilities/NightlyRoutine.cs
+++ b/src/bnhtrade.Core/Logic/Utilities/NightlyRoutine.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
namespace bnhtrade.Core.Logic.Utilities
@@ -14,8 +15,23 @@ namespace bnhtrade.Core.Logic.Utilities
{
}
+ public async Task IsServerOnlineAsync()
+ {
+ using var cts = new CancellationTokenSource();
+ Console.CancelKeyPress += (sender, e) => { e.Cancel = true; cts.Cancel(); };
+
+ return await new bnhtrade.Core.Logic.Utilities.SqlServerPing()
+ .WaitForServerAsync(timeout: 15, maxAttempts: 60, cts.Token);
+ }
+
public void DownloadAll()
{
+ if (!IsServerOnlineAsync().GetAwaiter().GetResult())
+ {
+ Console.WriteLine("Server is not online, skipping nightly scheduled tasks.");
+ return;
+ }
+
log.LogInformation("Nightly scheduled tasks started.");
bool stockUpdate = false;
diff --git a/src/bnhtrade.Core/Logic/Utilities/SqlServerPing.cs b/src/bnhtrade.Core/Logic/Utilities/SqlServerPing.cs
new file mode 100644
index 0000000..3762cd6
--- /dev/null
+++ b/src/bnhtrade.Core/Logic/Utilities/SqlServerPing.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace bnhtrade.Core.Logic.Utilities
+{
+ ///
+ /// Polls SQL Server until it comes online, max attempts are reached, or the operation is cancelled.
+ /// Intended for use on application startup when the remote SQL Server machine may still be booting.
+ ///
+ public class SqlServerPing
+ {
+ ///
+ /// Polls SQL Server until it comes online, max attempts are reached, or the cancellation token is triggered.
+ ///
+ /// Seconds each connection attempt waits before failing (1–300).
+ /// Maximum number of poll attempts. 0 = retry indefinitely.
+ /// Token to cancel the polling loop.
+ /// True when the server responds, false if max attempts reached or cancelled.
+ public async Task WaitForServerAsync(uint timeout = 10, uint maxAttempts = 6, CancellationToken cancellationToken = default)
+ {
+ return await new Data.Database.ServerPing().WaitForServerAsync(timeout, maxAttempts, cancellationToken);
+ }
+ }
+}
diff --git a/src/bnhtrade.Core/Model/Credentials/AmazonSPAPI.cs b/src/bnhtrade.Core/Model/Credentials/AmazonSPAPI.cs
deleted file mode 100644
index 2d90beb..0000000
--- a/src/bnhtrade.Core/Model/Credentials/AmazonSPAPI.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace bnhtrade.Core.Model.Credentials
-{
- public class AmazonSPAPI
- {
- public string AccessKey { get; private set; }
- public string SecretKey { get; private set; }
- public string RoleArn { get; private set; }
- public string ClientId { get; private set; }
- public string ClientSecret { get; private set; }
- public string RefreshToken { get; private set; }
-
- public AmazonSPAPI(string accessKey, string secretKey, string roleArn, string clientId, string clientSecret, string refreshToken)
- {
- this.AccessKey = accessKey;
- this.SecretKey = secretKey;
- this.RoleArn = roleArn;
- this.ClientId = clientId;
- this.ClientSecret = clientSecret;
- this.RefreshToken = refreshToken;
- }
- }
-}
diff --git a/src/bnhtrade.Core/Model/Credentials/bnhtradeDB.cs b/src/bnhtrade.Core/Model/Credentials/bnhtradeDB.cs
deleted file mode 100644
index 5fed5f3..0000000
--- a/src/bnhtrade.Core/Model/Credentials/bnhtradeDB.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace bnhtrade.Core.Model.Credentials
-{
- public class bnhtradeDB
- {
- public string DataSource { get; private set; }
-
- public string UserId { get; private set; }
-
- public string UserPassword { get; private set; }
-
- public string InitialCatalog { get; private set; } = "e2A";
-
- public bool PersistSecurityInfo { get; private set; } = true;
-
- public bool MultipleActiveResultSets { get; private set; } = true;
-
- public uint ConnectionTimeout { get; private set; }
-
- public string ConnectionString
- {
- get
- {
- return "Data Source=" + DataSource + ";Initial Catalog=" + InitialCatalog + ";Persist Security Info=" + PersistSecurityInfo.ToString()
- + ";User ID=" + UserId + ";Password=" + UserPassword + ";MultipleActiveResultSets=" + MultipleActiveResultSets.ToString()
- + ";Connect Timeout=" + ConnectionTimeout + ";Encrypt=True";
- }
- }
-
- public bnhtradeDB (string source, string userId, string userPassword, uint connectionTimeout = 30)
- {
- this.DataSource = source;
- this.UserId = userId;
- this.UserPassword = userPassword;
- this.ConnectionTimeout = connectionTimeout;
- }
- }
-}
diff --git a/src/bnhtrade.ScheduledTasks/Program.cs b/src/bnhtrade.ScheduledTasks/Program.cs
index 890d547..77a3474 100644
--- a/src/bnhtrade.ScheduledTasks/Program.cs
+++ b/src/bnhtrade.ScheduledTasks/Program.cs
@@ -2,6 +2,7 @@
using System;
using System.Configuration;
using System.Threading;
+using System.Threading.Tasks;
using System.Transactions;
namespace bnhtradeScheduledTasks
@@ -12,7 +13,7 @@ namespace bnhtradeScheduledTasks
{
}
- static void Main(string[] args)
+ static async Task Main(string[] args)
{
if (OperatingSystem.IsWindows())
{
@@ -310,7 +311,7 @@ namespace bnhtradeScheduledTasks
Console.WriteLine(consoleHeader);
Console.WriteLine("Main Menu > Dev Funcions");
Console.WriteLine();
- Console.WriteLine("<1> Test some randon function I've set here");
+ Console.WriteLine("<1> Ping SQL Server");
Console.WriteLine("<2> Test Account");
Console.WriteLine("<3> Test Export");
Console.WriteLine("<4> Test Import");
@@ -334,7 +335,16 @@ namespace bnhtradeScheduledTasks
{
Console.Clear();
- var obj = new bnhtrade.Core.Test.Amazon.SP_API.VariousCalls();
+ using var cts = new CancellationTokenSource();
+ Console.CancelKeyPress += (sender, e) => { e.Cancel = true; cts.Cancel(); };
+
+ bool online = await new bnhtrade.Core.Logic.Utilities.SqlServerPing()
+ .WaitForServerAsync(timeout: 10, maxAttempts: 10, cts.Token);
+
+ if (!online)
+ {
+ Console.WriteLine("Could not connect to SQL Server.");
+ }
Console.WriteLine("Complete, press any key to continue...");
Console.ReadKey();