diff --git a/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationAttribute.cs b/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationAttribute.cs
index 59f1464..b2b2cb6 100644
--- a/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationAttribute.cs
+++ b/AMWD.Common.AspNetCore/BasicAuthentication/BasicAuthenticationAttribute.cs
@@ -56,7 +56,7 @@ namespace AMWD.Common.AspNetCore.BasicAuthentication
if (context.Result != null)
return;
- if (context.HttpContext.Request.Headers.ContainsKey("Authorization"))
+ if (!context.HttpContext.Request.Headers.ContainsKey("Authorization"))
{
SetAuthenticateRequest(context);
return;
diff --git a/AMWD.Common.EntityFrameworkCore/Extensions/DatabaseFacadeExtensions.cs b/AMWD.Common.EntityFrameworkCore/Extensions/DatabaseFacadeExtensions.cs
index d4fecd5..61f63a3 100644
--- a/AMWD.Common.EntityFrameworkCore/Extensions/DatabaseFacadeExtensions.cs
+++ b/AMWD.Common.EntityFrameworkCore/Extensions/DatabaseFacadeExtensions.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;
@@ -28,18 +29,24 @@ namespace Microsoft.EntityFrameworkCore
if (database == null)
throw new ArgumentNullException(nameof(database));
- var options = new DatabaseMigrationOptions();
- optionsAction?.Invoke(options);
-
if (database.GetProviderType() == DatabaseProvider.InMemory)
return true;
+ var options = new DatabaseMigrationOptions();
+ optionsAction?.Invoke(options);
+
if (string.IsNullOrWhiteSpace(options.MigrationsTableName))
throw new ArgumentNullException(nameof(options.MigrationsTableName), $"The property {nameof(options.MigrationsTableName)} of the {nameof(options)} parameter is required.");
if (string.IsNullOrWhiteSpace(options.Path))
throw new ArgumentNullException(nameof(options.Path), $"The property {nameof(options.Path)} of the {nameof(options)} parameter is required.");
+ await database.WaitAvailableAsync(opts =>
+ {
+ opts.WaitDelay = options.WaitDelay;
+ opts.Logger = options.Logger;
+ }, cancellationToken);
+
var connection = database.GetDbConnection();
try
{
@@ -52,7 +59,58 @@ namespace Microsoft.EntityFrameworkCore
}
finally
{
- connection.Close();
+ await connection.CloseAsync();
+ }
+ }
+
+ ///
+ /// Waits until the database connection is available.
+ ///
+ /// The database connection.
+ /// An action to set additional options.
+ /// A cancellation token.
+ /// An awaitable task to wait until the database is available.
+ public static async Task WaitAvailableAsync(this DatabaseFacade database, Action optionsAction = null, CancellationToken cancellationToken = default)
+ {
+ if (database == null)
+ throw new ArgumentNullException(nameof(database));
+
+ if (database.GetProviderType() == DatabaseProvider.InMemory)
+ return;
+
+ var options = new DatabaseMigrationOptions();
+ optionsAction?.Invoke(options);
+
+ options.Logger?.LogInformation("Waiting for a database connection");
+ var connection = database.GetDbConnection();
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ try
+ {
+ await connection.OpenAsync(cancellationToken);
+ options.Logger?.LogInformation("Database connection available");
+ return;
+ }
+ catch
+ {
+ // keep things quiet
+ try
+ {
+ await Task.Delay(options.WaitDelay, cancellationToken);
+ }
+ catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
+ {
+ return;
+ }
+ catch
+ {
+ // keep things quiet
+ }
+ }
+ finally
+ {
+ await connection.CloseAsync();
+ }
}
}
diff --git a/AMWD.Common.EntityFrameworkCore/Utilities/DatabaseMigrationOptions.cs b/AMWD.Common.EntityFrameworkCore/Utilities/DatabaseMigrationOptions.cs
index 32a44b6..504fc94 100644
--- a/AMWD.Common.EntityFrameworkCore/Utilities/DatabaseMigrationOptions.cs
+++ b/AMWD.Common.EntityFrameworkCore/Utilities/DatabaseMigrationOptions.cs
@@ -1,4 +1,5 @@
-using System.Reflection;
+using System;
+using System.Reflection;
using Microsoft.Extensions.Logging;
namespace Microsoft.EntityFrameworkCore
@@ -27,5 +28,10 @@ namespace Microsoft.EntityFrameworkCore
/// Gets or sets the source assembly for embedded files.
///
public Assembly SourceAssembly { get; set; }
+
+ ///
+ /// Gets or sets a delay to wait before a connect retry.
+ ///
+ public TimeSpan WaitDelay { get; set; } = TimeSpan.FromSeconds(1);
}
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9f99553..44f1816 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,9 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [Unreleased](https://git.am-wd.de/AM.WD/common/compare/v1.4.0...master) - 0000-00-00
+## [Unreleased](https://git.am-wd.de/AM.WD/common/compare/v1.4.1...master) - 0000-00-00
+_nothing changed yet_
+
+
+## [v1.4.1](https://git.am-wd.de/AM.WD/common/compare/v1.4.0...v1.4.1) - 2022-03-23
### Added
- `IntegrityHashTagHelper` can be used with `asp-integrity="true|false"` and `asp-integrity-strength="256|384|512"`
+- `WaitAvailableAsync` can be used to wait for a database connection to be available.
+
+### Changed
+- `ApplyMigrationsAsync` uses `WaitAvailableAsync` internally.
### Fixed
- `InMemory` database provider does not fail on "migration" (none possible)