From 4cd9ed36e340afa6f841d5a5bc4875bde1ec45d7 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Tue, 12 May 2026 21:46:45 +0200 Subject: [PATCH 1/3] Order Quartz lifecycle initializers --- Directory.Packages.props | 16 ++++++++-------- .../ShellFeatures/QuartzMySqlFeature.cs | 3 +-- .../ShellFeatures/QuartzPostgreSqlFeature.cs | 3 +-- .../ShellFeatures/QuartzSqlServerFeature.cs | 3 +-- .../ShellFeatures/QuartzSqliteFeature.cs | 4 +--- .../ShellFeatures/QuartzShellLifecycleHandler.cs | 1 + .../ShellFeatures/QuartzFeatureTests.cs | 11 +++++++++++ 7 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ea39e0d4..b4ac0a80 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -141,13 +141,13 @@ - - - - - - - + + + + + + + @@ -243,4 +243,4 @@ - \ No newline at end of file + diff --git a/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.MySql/ShellFeatures/QuartzMySqlFeature.cs b/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.MySql/ShellFeatures/QuartzMySqlFeature.cs index 2af0f17b..ecb4c03a 100644 --- a/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.MySql/ShellFeatures/QuartzMySqlFeature.cs +++ b/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.MySql/ShellFeatures/QuartzMySqlFeature.cs @@ -35,8 +35,7 @@ public void ConfigureServices(IServiceCollection services) else services.AddDbContextFactory(Configure); - services.AddTransient(sp => - new EfCoreMigrationHandler(sp.GetRequiredService>())); + services.AddShellInitializer(LifecyclePhase.Prepare, order: 100); services.AddQuartz(quartz => { diff --git a/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.PostgreSql/ShellFeatures/QuartzPostgreSqlFeature.cs b/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.PostgreSql/ShellFeatures/QuartzPostgreSqlFeature.cs index c2b9ba79..52e2eb0d 100644 --- a/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.PostgreSql/ShellFeatures/QuartzPostgreSqlFeature.cs +++ b/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.PostgreSql/ShellFeatures/QuartzPostgreSqlFeature.cs @@ -35,8 +35,7 @@ public void ConfigureServices(IServiceCollection services) else services.AddDbContextFactory(Configure); - services.AddTransient(sp => - new EfCoreMigrationHandler(sp.GetRequiredService>())); + services.AddShellInitializer(LifecyclePhase.Prepare, order: 100); services.AddQuartz(quartz => { diff --git a/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.SqlServer/ShellFeatures/QuartzSqlServerFeature.cs b/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.SqlServer/ShellFeatures/QuartzSqlServerFeature.cs index 538385a6..87943402 100644 --- a/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.SqlServer/ShellFeatures/QuartzSqlServerFeature.cs +++ b/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.SqlServer/ShellFeatures/QuartzSqlServerFeature.cs @@ -35,8 +35,7 @@ public void ConfigureServices(IServiceCollection services) else services.AddDbContextFactory(Configure); - services.AddTransient(sp => - new EfCoreMigrationHandler(sp.GetRequiredService>())); + services.AddShellInitializer(LifecyclePhase.Prepare, order: 100); // AddQuartz is additive — layers the persistent store onto the base // AddQuartz call already made by QuartzFeature (which ran first via DependsOn). diff --git a/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.Sqlite/ShellFeatures/QuartzSqliteFeature.cs b/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.Sqlite/ShellFeatures/QuartzSqliteFeature.cs index ce3263cd..e16ab026 100644 --- a/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.Sqlite/ShellFeatures/QuartzSqliteFeature.cs +++ b/src/modules/scheduling/Elsa.Scheduling.Quartz.EFCore.Sqlite/ShellFeatures/QuartzSqliteFeature.cs @@ -38,9 +38,7 @@ public void ConfigureServices(IServiceCollection services) else services.AddDbContextFactory(Configure); - services.AddTransient(sp => - new EfCoreMigrationHandler( - sp.GetRequiredService>())); + services.AddShellInitializer(LifecyclePhase.Prepare, order: 100); } public void PostConfigureServices(IServiceCollection services) diff --git a/src/modules/scheduling/Elsa.Scheduling.Quartz/ShellFeatures/QuartzShellLifecycleHandler.cs b/src/modules/scheduling/Elsa.Scheduling.Quartz/ShellFeatures/QuartzShellLifecycleHandler.cs index 843e409b..66fb2e4a 100644 --- a/src/modules/scheduling/Elsa.Scheduling.Quartz/ShellFeatures/QuartzShellLifecycleHandler.cs +++ b/src/modules/scheduling/Elsa.Scheduling.Quartz/ShellFeatures/QuartzShellLifecycleHandler.cs @@ -15,6 +15,7 @@ namespace Elsa.Scheduling.Quartz.ShellFeatures; /// so the AwaitApplicationStarted deferral logic from QuartzHostedService /// is intentionally omitted — it is irrelevant in this context. /// +[LifecycleOrder(LifecyclePhase.Start, 100)] public class QuartzShellLifecycleHandler( IQuartzSchedulerFactory schedulerFactory, IHostApplicationLifetime appLifetime, diff --git a/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/ShellFeatures/QuartzFeatureTests.cs b/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/ShellFeatures/QuartzFeatureTests.cs index 5210ef2d..f576dae1 100644 --- a/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/ShellFeatures/QuartzFeatureTests.cs +++ b/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/ShellFeatures/QuartzFeatureTests.cs @@ -2,6 +2,7 @@ using CShells.Lifecycle; using Elsa.Scheduling.Quartz.ShellFeatures; using Microsoft.Extensions.DependencyInjection; +using System.Reflection; namespace Elsa.Scheduling.Quartz.UnitTests.ShellFeatures; @@ -26,6 +27,16 @@ public void SchedulerInitializer_IsRegisteredDuringPostConfigure() Assert.NotNull(initializerRegistrations[1].ImplementationFactory); } + [Fact] + public void SchedulerInitializer_RunsInStartPhase() + { + var order = typeof(QuartzShellLifecycleHandler).GetCustomAttribute(); + + Assert.NotNull(order); + Assert.Equal(LifecyclePhase.Start, order.Phase); + Assert.Equal(100, order.Order); + } + private sealed class DependentInitializer : IShellInitializer { public Task InitializeAsync(CancellationToken cancellationToken = default) => Task.CompletedTask; From ab471316bc31a0e58d4695dd224d5d8d76f54681 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Tue, 12 May 2026 21:51:22 +0200 Subject: [PATCH 2/3] Use stable CShells 0.0.21 --- Directory.Packages.props | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index b4ac0a80..0ae482e5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -141,13 +141,13 @@ - - - - - - - + + + + + + + From 40cee1f372b8fe64a3937d96aca15d5d6f54e4db Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Tue, 12 May 2026 21:56:21 +0200 Subject: [PATCH 3/3] Cover Quartz store initializer ordering --- .../Elsa.Scheduling.Quartz.UnitTests.csproj | 4 +++ .../ShellFeatures/QuartzFeatureTests.cs | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/Elsa.Scheduling.Quartz.UnitTests.csproj b/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/Elsa.Scheduling.Quartz.UnitTests.csproj index c9bdbb85..c3e2a940 100644 --- a/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/Elsa.Scheduling.Quartz.UnitTests.csproj +++ b/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/Elsa.Scheduling.Quartz.UnitTests.csproj @@ -2,6 +2,10 @@ + + + + diff --git a/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/ShellFeatures/QuartzFeatureTests.cs b/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/ShellFeatures/QuartzFeatureTests.cs index f576dae1..51399a8e 100644 --- a/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/ShellFeatures/QuartzFeatureTests.cs +++ b/test/modules/scheduling/Elsa.Scheduling.Quartz.UnitTests/ShellFeatures/QuartzFeatureTests.cs @@ -1,5 +1,9 @@ using CShells.Features; using CShells.Lifecycle; +using Elsa.Scheduling.Quartz.EFCore.MySql.ShellFeatures; +using Elsa.Scheduling.Quartz.EFCore.PostgreSql.ShellFeatures; +using Elsa.Scheduling.Quartz.EFCore.SqlServer.ShellFeatures; +using Elsa.Scheduling.Quartz.EFCore.Sqlite.ShellFeatures; using Elsa.Scheduling.Quartz.ShellFeatures; using Microsoft.Extensions.DependencyInjection; using System.Reflection; @@ -37,6 +41,32 @@ public void SchedulerInitializer_RunsInStartPhase() Assert.Equal(100, order.Order); } + [Theory] + [MemberData(nameof(QuartzStoreFeatures))] + public void StoreMigrationInitializer_RunsInPreparePhase(IShellFeature feature) + { + var services = new ServiceCollection(); + + feature.ConfigureServices(services); + + var initializer = Assert.Single(services, x => x.ServiceType == typeof(IShellInitializer)); + var registrationDescriptor = Assert.Single(services, x => x.ServiceType == typeof(ShellInitializerRegistration)); + var registration = Assert.IsType(registrationDescriptor.ImplementationInstance); + + Assert.NotNull(initializer.ImplementationFactory); + Assert.Equal(LifecyclePhase.Prepare, registration.Phase); + Assert.Equal(100, registration.Order); + Assert.True(typeof(IShellInitializer).IsAssignableFrom(registration.InitializerType)); + } + + public static TheoryData QuartzStoreFeatures() => new() + { + new QuartzMySqlFeature(), + new QuartzPostgreSqlFeature(), + new QuartzSqlServerFeature(), + new QuartzSqliteFeature() + }; + private sealed class DependentInitializer : IShellInitializer { public Task InitializeAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;