From e38864b32b82edf04e28b3bea7afd7a15db07ed5 Mon Sep 17 00:00:00 2001 From: Andreas Mueller Date: Wed, 23 Mar 2022 20:50:20 +0100 Subject: [PATCH] Adding WaitAvailable to EFCore extensions --- .../BasicAuthenticationAttribute.cs | 2 +- .../Extensions/DatabaseFacadeExtensions.cs | 66 +++++++++++++++++-- .../Utilities/DatabaseMigrationOptions.cs | 8 ++- CHANGELOG.md | 10 ++- 4 files changed, 79 insertions(+), 7 deletions(-) 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)