Add factory-based AddQuartz() overload with IServiceProvider access#3007
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds new DI extension overloads for AddQuartz(...) that provide IServiceProvider access during Quartz configuration by deferring the user’s configuration delegate until QuartzOptions is materialized. It introduces internal plumbing to capture deferred registrations (jobs/triggers/calendars/listeners) and applies them during scheduler initialization for both default and named schedulers.
Changes:
- Add
AddQuartzoverloads acceptingAction<IServiceCollectionQuartzConfigurator, IServiceProvider>(default + named + properties variants) with deferred execution via options configuration. - Introduce
DeferredQuartzConfiguration+DeferredServiceCollectionto apply deferred configurator operations intoQuartzOptions. - Update scheduler factories to process deferred listeners/calendars; add extensive unit/integration-style tests for deferred behavior.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Quartz.Extensions.DependencyInjection/ServiceCollectionExtensions.cs | Adds new factory-based AddQuartz overloads that defer configuration until options are resolved. |
| src/Quartz.Extensions.DependencyInjection/DeferredQuartzConfiguration.cs | Implements deferred IConfigureNamedOptions<QuartzOptions> execution with IServiceProvider access. |
| src/Quartz.Extensions.DependencyInjection/DeferredServiceCollection.cs | Wraps IServiceCollection to intercept Quartz-related registrations and apply them directly to QuartzOptions. |
| src/Quartz.Extensions.DependencyInjection/QuartzOptions.cs | Adds internal lists to store deferred listeners/calendars. |
| src/Quartz.Extensions.DependencyInjection/ServiceCollectionSchedulerFactory.cs | Applies deferred listeners/calendars for the default scheduler during initialization. |
| src/Quartz.Extensions.DependencyInjection/NamedSchedulerFactory.cs | Applies deferred listeners/calendars for named schedulers; extracts listener creation into helper. |
| src/Quartz.Extensions.DependencyInjection/ListenerCreationHelper.cs | Shared helper for creating listener instances from configuration records. |
| src/Quartz.Tests.Unit/Extensions/DependencyInjection/DeferredQuartzConfigurationTests.cs | New test suite covering deferred properties, registrations, named scheduler isolation, and end-to-end scheduler creation scenarios. |
…1617) Add new AddQuartz overloads that accept Action<IServiceCollectionQuartzConfigurator, IServiceProvider>, allowing users to resolve DI services (e.g. connection string providers) during Quartz configuration. The configuration delegate is deferred until IOptions<QuartzOptions>.Value is accessed, at which point IServiceProvider is available. Supports default schedulers, named schedulers, and NameValueCollection property overloads. All configurator operations (properties, AddJob, AddTrigger, ScheduleJob, AddCalendar, listeners) work transparently in the deferred context via a DeferredServiceCollection wrapper. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Suppress IJobListener/ITriggerListener interceptions in DeferredServiceCollection since the *Configuration entries (with matchers) already handle them - Discard unhandled registrations instead of adding to the original IServiceCollection - Add duplicate-listener regression test assertion Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Make DeferredServiceCollection fully immutable: indexer setter, Clear, Remove, RemoveAt are no-ops; IsReadOnly returns true - Add doc comment on ResolveInstance explaining why null for type-only descriptors is safe - Replace verbose 21-method TestSchedulerListener and TestJobListener with SchedulerListenerSupport / JobListenerSupport base classes Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Only suppress the interface registration when a matching *Configuration has already been captured. If no configuration exists (e.g., a listener registered only via the interface), create a fallback configuration so the listener is never silently dropped. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Revert conditional IJobListener/ITriggerListener suppression to unconditional: the conditional check failed for factory/instance-based descriptors where ImplementationType is null, causing duplicates. Unconditional suppression is safe because Services is internal and all listener registrations go through configurator methods that always emit the paired *Configuration. - Re-force scheduler instance name for named schedulers after deferred lambda runs, matching the guard in the immediate AddQuartz overload. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
lahma
added a commit
that referenced
this pull request
Apr 5, 2026
Port of 3.x PR #3007 to main (4.x). Adds AddQuartz overloads accepting Action<IServiceCollectionQuartzConfigurator, IServiceProvider> for deferred configuration with access to DI services. Adapted for 4.x: files in src/Quartz/Configuration/, ValueTask returns, collection expressions, ArgumentNullException.ThrowIfNull, no MicrosoftLoggingProvider setup, updated ADO provider API signatures. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
2 tasks
lahma
added a commit
that referenced
this pull request
Apr 5, 2026
* Port factory-based AddQuartz() with IServiceProvider to 4.x (#1617) Port of 3.x PR #3007 to main (4.x). Adds AddQuartz overloads accepting Action<IServiceCollectionQuartzConfigurator, IServiceProvider> for deferred configuration with access to DI services. Adapted for 4.x: files in src/Quartz/Configuration/, ValueTask returns, collection expressions, ArgumentNullException.ThrowIfNull, no MicrosoftLoggingProvider setup, updated ADO provider API signatures. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * Add test proving UsePersistentStore property configuration works in deferred lambda Verifies that connection string, job store type, driver delegate, and clustering properties are all correctly set when configured through the deferred AddQuartz overload — covering the primary use case from #1617. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Closes #1617
AddQuartzoverloads acceptingAction<IServiceCollectionQuartzConfigurator, IServiceProvider>, allowing users to resolve DI services during Quartz configuration (e.g. to obtain connection strings from aDatabaseConnectionFactory)IOptions<QuartzOptions>.Valueis accessed, at which pointIServiceProvideris availableAddJob,AddTrigger,ScheduleJob,AddCalendar, listeners) work transparently in the deferred context via aDeferredServiceCollectionwrapperNameValueCollectionproperty overloadsNew API
Implementation
DeferredQuartzConfiguration—IConfigureNamedOptions<QuartzOptions>that defers the user's configure lambdaDeferredServiceCollection—IServiceCollectionwrapper that intercepts service registrations (jobs, triggers, listeners, calendars) and applies them directly toQuartzOptionsListenerCreationHelper— Shared static helper extracted fromNamedSchedulerFactoryfor creating listener instances from configuration objectsServiceCollectionSchedulerFactoryandNamedSchedulerFactoryupdated to process deferred listeners/calendarsTest plan
IServiceProvider(scheduler name, connection strings,IOptions<T>)AddJob/AddTrigger/ScheduleJob/AddCalendarin deferred lambda🤖 Generated with Claude Code