diff --git a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs index c1d2502..ec2ccc2 100644 --- a/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs +++ b/Shuttle.Core.Data.Tests/DatabaseContextFactoryFixture.cs @@ -1,4 +1,8 @@ -using NUnit.Framework; +using System; +using System.Data; +using System.Threading; +using Moq; +using NUnit.Framework; namespace Shuttle.Core.Data.Tests { @@ -37,5 +41,26 @@ public void Should_be_able_to_get_an_existing_database_context() Assert.AreSame(existingContext.Connection, context.Connection); } } + + [Test] + public void Should_be_able_to_check_connection_availability() + { + var databaseContextFactory = new Mock(); + + Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.True); + Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.True); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.True); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.True); + + databaseContextFactory.Setup(m => m.Create()).Throws(new Exception()); + databaseContextFactory.Setup(m => m.Create(It.IsAny())).Throws(new Exception()); + databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); + databaseContextFactory.Setup(m => m.Create(It.IsAny(), It.IsAny())).Throws(new Exception()); + + Assert.That(databaseContextFactory.Object.IsAvailable(new CancellationToken(), 0, 0), Is.False); + Assert.That(databaseContextFactory.Object.IsAvailable("name", new CancellationToken(), 0, 0), Is.False); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", new Mock().Object, new CancellationToken(), 0, 0), Is.False); + Assert.That(databaseContextFactory.Object.IsAvailable("provider-name", "connection-string", new CancellationToken(), 0, 0), Is.False); + } } } \ No newline at end of file diff --git a/Shuttle.Core.Data/.package/package.nuspec b/Shuttle.Core.Data/.package/package.nuspec index 182bf50..03d582c 100644 --- a/Shuttle.Core.Data/.package/package.nuspec +++ b/Shuttle.Core.Data/.package/package.nuspec @@ -3,7 +3,7 @@ Shuttle.Core.Data - 12.0.2 + 12.1.0 Eben Roux Eben Roux BSD-3-Clause diff --git a/Shuttle.Core.Data/DatabaseContextFactoryExtensions.cs b/Shuttle.Core.Data/DatabaseContextFactoryExtensions.cs new file mode 100644 index 0000000..c400bf4 --- /dev/null +++ b/Shuttle.Core.Data/DatabaseContextFactoryExtensions.cs @@ -0,0 +1,93 @@ +using System; +using System.Data; +using System.Threading; +using Shuttle.Core.Contract; + +namespace Shuttle.Core.Data +{ + public static class DatabaseContextFactoryExtensions + { + public static bool IsAvailable(this IDatabaseContextFactory databaseContextFactory, + CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) + { + Guard.AgainstNull(databaseContextFactory, nameof(databaseContextFactory)); + + return IsAvailable(() => + { + using (databaseContextFactory.Create()) + { + } + }, cancellationToken, retries, secondsBetweenRetries); + } + + public static bool IsAvailable(this IDatabaseContextFactory databaseContextFactory, string name, + CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) + { + Guard.AgainstNull(databaseContextFactory, nameof(databaseContextFactory)); + + return IsAvailable(() => + { + using (databaseContextFactory.Create(name)) + { + } + }, cancellationToken, retries, secondsBetweenRetries); + } + + public static bool IsAvailable(this IDatabaseContextFactory databaseContextFactory, string providerName, IDbConnection dbConnection, + CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) + { + Guard.AgainstNull(databaseContextFactory, nameof(databaseContextFactory)); + + return IsAvailable(() => + { + using (databaseContextFactory.Create(providerName, dbConnection)) + { + } + }, cancellationToken, retries, secondsBetweenRetries); + } + + public static bool IsAvailable(this IDatabaseContextFactory databaseContextFactory, string providerName, string connectionString, + CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) + { + Guard.AgainstNull(databaseContextFactory, nameof(databaseContextFactory)); + + return IsAvailable(() => + { + using (databaseContextFactory.Create(providerName, connectionString)) + { + } + }, cancellationToken, retries, secondsBetweenRetries); + } + + private static bool IsAvailable(Action action, CancellationToken cancellationToken, int retries = 4, int secondsBetweenRetries = 15) + { + var attempt = 0; + + do + { + try + { + action.Invoke(); + + break; + } + catch + { + attempt++; + + if (attempt < retries) + { + var wait = DateTime.Now.AddSeconds(secondsBetweenRetries); + + while (!cancellationToken.IsCancellationRequested && DateTime.Now < wait) + { + Thread.Sleep(250); + } + } + } + } while (attempt < retries); + + return attempt <= retries; + } + } +} \ No newline at end of file diff --git a/Shuttle.Core.Data/Properties/AssemblyInfo.cs b/Shuttle.Core.Data/Properties/AssemblyInfo.cs index 8ef3129..5eb4f8d 100644 --- a/Shuttle.Core.Data/Properties/AssemblyInfo.cs +++ b/Shuttle.Core.Data/Properties/AssemblyInfo.cs @@ -13,10 +13,10 @@ [assembly: AssemblyTitle(".NET Standard")] #endif -[assembly: AssemblyVersion("12.0.2.0")] +[assembly: AssemblyVersion("12.1.0.0")] [assembly: AssemblyCopyright("Copyright (c) 2022, Eben Roux")] [assembly: AssemblyProduct("Shuttle.Core.Data")] [assembly: AssemblyCompany("Eben Roux")] [assembly: AssemblyConfiguration("Release")] -[assembly: AssemblyInformationalVersion("12.0.2")] +[assembly: AssemblyInformationalVersion("12.1.0")] [assembly: ComVisible(false)] \ No newline at end of file