Table of Contents

Class QuartzServiceCollectionExtensions

Namespace
Quartz
Assembly
Quartz.dll

Registers Quartz schedulers, and the hosted service that runs them, into an application's container.

public static class QuartzServiceCollectionExtensions
Inheritance
QuartzServiceCollectionExtensions
Inherited Members

Remarks

The other half of this class lives beside the hosted service it registers, in Hosting/QuartzServiceCollectionExtensions.cs. What a scheduler carries — jobs, triggers, calendars — is added through QuartzBuilderExtensions, which extends IQuartzBuilder rather than IServiceCollection.

Methods

AddQuartz(IServiceCollection, IConfiguration, Action<IQuartzBuilder>?)

Registers a Quartz scheduler from a configuration section.

public static IServiceCollection AddQuartz(this IServiceCollection services, IConfiguration configuration, Action<IQuartzBuilder>? configure = null)

Parameters

services IServiceCollection
configuration IConfiguration
configure Action<IQuartzBuilder>

Returns

IServiceCollection

Remarks

Hierarchical sections such as Scheduler and ThreadPool bind onto the typed options directly. Flat quartz.* keys are still accepted and mean the same thing. Several schedulers described by one section are registered with AddQuartzSchedulers(IServiceCollection, IConfiguration, Action<IQuartzBuilder>?).

A scheduler is described by six things, applied in this order — which is what decides who wins when two of them say something about the same setting:

  1. The flat quartz.* properties are recorded in Properties, so the parts that read the property bag directly — plugins, execution limits — can see them.
  2. Those properties are translated into typed options.
  3. Properties contributed later by configuring QuartzOptions are translated too, which is what makes services.Configure<QuartzOptions> equivalent to passing them here.
  4. The configure callback runs. Options are last-wins, so anything it sets is applied over the property-derived values: configuration written in code beats a string.
  5. The implementations the properties name — a job store type, a thread pool type, a serializer — are registered. Registration is first-wins, so this comes after the callback: a store chosen by UsePersistentStore has to beat a leftover quartz.jobStore.type from an old configuration file. Code beats strings in both directions, opposite orders notwithstanding.
  6. Quartz's own defaults are registered last, so an explicitly configured job store, thread pool or serializer is never beaten to the registration by the fallback it was meant to replace.

The scheduler this registers is the container's unkeyed IScheduler, which is what GetRequiredService<IScheduler>() answers with. Registering it when something else already owns that slot throws an InvalidOperationException rather than quietly leaving "the scheduler" meaning the other one; AddQuartzHttpClient is the one in the box that takes it. The other order is fine and needs no thought: AddQuartz() followed by AddQuartzHttpClient(…) leaves the local default scheduler unkeyed and the remote one reachable as GetRequiredKeyedService<IScheduler>(schedulerName).

AddQuartz(IServiceCollection, Action<IQuartzBuilder>?)

Registers a Quartz scheduler.

public static IServiceCollection AddQuartz(this IServiceCollection services, Action<IQuartzBuilder>? configure = null)

Parameters

services IServiceCollection
configure Action<IQuartzBuilder>

Returns

IServiceCollection

Remarks

Hierarchical sections such as Scheduler and ThreadPool bind onto the typed options directly. Flat quartz.* keys are still accepted and mean the same thing. Several schedulers described by one section are registered with AddQuartzSchedulers(IServiceCollection, IConfiguration, Action<IQuartzBuilder>?).

A scheduler is described by six things, applied in this order — which is what decides who wins when two of them say something about the same setting:

  1. The flat quartz.* properties are recorded in Properties, so the parts that read the property bag directly — plugins, execution limits — can see them.
  2. Those properties are translated into typed options.
  3. Properties contributed later by configuring QuartzOptions are translated too, which is what makes services.Configure<QuartzOptions> equivalent to passing them here.
  4. The configure callback runs. Options are last-wins, so anything it sets is applied over the property-derived values: configuration written in code beats a string.
  5. The implementations the properties name — a job store type, a thread pool type, a serializer — are registered. Registration is first-wins, so this comes after the callback: a store chosen by UsePersistentStore has to beat a leftover quartz.jobStore.type from an old configuration file. Code beats strings in both directions, opposite orders notwithstanding.
  6. Quartz's own defaults are registered last, so an explicitly configured job store, thread pool or serializer is never beaten to the registration by the fallback it was meant to replace.

The scheduler this registers is the container's unkeyed IScheduler, which is what GetRequiredService<IScheduler>() answers with. Registering it when something else already owns that slot throws an InvalidOperationException rather than quietly leaving "the scheduler" meaning the other one; AddQuartzHttpClient is the one in the box that takes it. The other order is fine and needs no thought: AddQuartz() followed by AddQuartzHttpClient(…) leaves the local default scheduler unkeyed and the remote one reachable as GetRequiredKeyedService<IScheduler>(schedulerName).

AddQuartz(IServiceCollection, IEnumerable<KeyValuePair<string, string?>>, Action<IQuartzBuilder>?)

Registers a Quartz scheduler, seeded with flat quartz.* properties.

public static IServiceCollection AddQuartz(this IServiceCollection services, IEnumerable<KeyValuePair<string, string?>> properties, Action<IQuartzBuilder>? configure = null)

Parameters

services IServiceCollection

The service collection to register into.

properties IEnumerable<KeyValuePair<string, string>>

The flat quartz.* properties, in the shape every dictionary already has — a Dictionary<TKey, TValue>, an IReadOnlyDictionary<TKey, TValue> and Properties all go in without a conversion step. They are checked against the keys Quartz reads, as they are on the standalone builder, so a misspelling — or a key 4.0 stopped reading — is reported rather than silently ignored. Set quartz.checkConfiguration to false to allow keys of your own.

configure Action<IQuartzBuilder>

Configures the scheduler.

Returns

IServiceCollection

Remarks

Hierarchical sections such as Scheduler and ThreadPool bind onto the typed options directly. Flat quartz.* keys are still accepted and mean the same thing. Several schedulers described by one section are registered with AddQuartzSchedulers(IServiceCollection, IConfiguration, Action<IQuartzBuilder>?).

A scheduler is described by six things, applied in this order — which is what decides who wins when two of them say something about the same setting:

  1. The flat quartz.* properties are recorded in Properties, so the parts that read the property bag directly — plugins, execution limits — can see them.
  2. Those properties are translated into typed options.
  3. Properties contributed later by configuring QuartzOptions are translated too, which is what makes services.Configure<QuartzOptions> equivalent to passing them here.
  4. The configure callback runs. Options are last-wins, so anything it sets is applied over the property-derived values: configuration written in code beats a string.
  5. The implementations the properties name — a job store type, a thread pool type, a serializer — are registered. Registration is first-wins, so this comes after the callback: a store chosen by UsePersistentStore has to beat a leftover quartz.jobStore.type from an old configuration file. Code beats strings in both directions, opposite orders notwithstanding.
  6. Quartz's own defaults are registered last, so an explicitly configured job store, thread pool or serializer is never beaten to the registration by the fallback it was meant to replace.

The scheduler this registers is the container's unkeyed IScheduler, which is what GetRequiredService<IScheduler>() answers with. Registering it when something else already owns that slot throws an InvalidOperationException rather than quietly leaving "the scheduler" meaning the other one; AddQuartzHttpClient is the one in the box that takes it. The other order is fine and needs no thought: AddQuartz() followed by AddQuartzHttpClient(…) leaves the local default scheduler unkeyed and the remote one reachable as GetRequiredKeyedService<IScheduler>(schedulerName).

AddQuartz(IServiceCollection, NameValueCollection, Action<IQuartzBuilder>?)

Registers a Quartz scheduler, seeded with flat quartz.* properties held in a NameValueCollection.

public static IServiceCollection AddQuartz(this IServiceCollection services, NameValueCollection properties, Action<IQuartzBuilder>? configure = null)

Parameters

services IServiceCollection

The service collection to register into.

properties NameValueCollection

The flat quartz.* properties.

configure Action<IQuartzBuilder>

Configures the scheduler.

Returns

IServiceCollection

Remarks

A NameValueCollection is what a caller migrating from 3.x already holds — it is what StdSchedulerFactory took — so it stays a single call. Everything else about it is AddQuartz(IServiceCollection, IEnumerable<KeyValuePair<string, string?>>, Action<IQuartzBuilder>?), which this forwards to.

AddQuartz(IServiceCollection, string, IConfiguration, Action<IQuartzBuilder>?)

Registers a named Quartz scheduler from a configuration section.

public static IServiceCollection AddQuartz(this IServiceCollection services, string name, IConfiguration configuration, Action<IQuartzBuilder>? configure = null)

Parameters

services IServiceCollection
name string
configuration IConfiguration
configure Action<IQuartzBuilder>

Returns

IServiceCollection

AddQuartz(IServiceCollection, string, Action<IQuartzBuilder>?)

Registers a named Quartz scheduler, so several independent schedulers can share a container.

public static IServiceCollection AddQuartz(this IServiceCollection services, string name, Action<IQuartzBuilder>? configure = null)

Parameters

services IServiceCollection
name string
configure Action<IQuartzBuilder>

Returns

IServiceCollection

Remarks

The name is the scheduler's instance name, the key its components are registered under, and the name of its options, so its registrations and its configuration always agree.

AddQuartz(IServiceCollection, string, IEnumerable<KeyValuePair<string, string?>>, Action<IQuartzBuilder>?)

Registers a named Quartz scheduler, seeded with flat quartz.* properties.

public static IServiceCollection AddQuartz(this IServiceCollection services, string name, IEnumerable<KeyValuePair<string, string?>> properties, Action<IQuartzBuilder>? configure = null)

Parameters

services IServiceCollection
name string
properties IEnumerable<KeyValuePair<string, string>>

The flat quartz.* properties, in the shape every dictionary already has — a Dictionary<TKey, TValue>, an IReadOnlyDictionary<TKey, TValue> and Properties all go in without a conversion step. They are checked against the keys Quartz reads, as they are on the standalone builder, so a misspelling — or a key 4.0 stopped reading — is reported rather than silently ignored. Set quartz.checkConfiguration to false to allow keys of your own.

configure Action<IQuartzBuilder>

Returns

IServiceCollection

AddQuartz(IServiceCollection, string, NameValueCollection, Action<IQuartzBuilder>?)

Registers a named Quartz scheduler, seeded with flat quartz.* properties held in a NameValueCollection.

public static IServiceCollection AddQuartz(this IServiceCollection services, string name, NameValueCollection properties, Action<IQuartzBuilder>? configure = null)

Parameters

services IServiceCollection
name string
properties NameValueCollection
configure Action<IQuartzBuilder>

Returns

IServiceCollection

Remarks

A NameValueCollection is what a caller migrating from 3.x already holds — it is what StdSchedulerFactory took — so it stays a single call. Everything else about it is AddQuartz(IServiceCollection, IEnumerable<KeyValuePair<string, string?>>, Action<IQuartzBuilder>?), which this forwards to.

AddQuartzHostedService(IServiceCollection, Action<QuartzHostedServiceOptions>?)

Adds a QuartzHostedService to the IServiceCollection, which starts and stops every scheduler in the container with the application.

public static IServiceCollection AddQuartzHostedService(this IServiceCollection services, Action<QuartzHostedServiceOptions>? configure = null)

Parameters

services IServiceCollection

The IServiceCollection.

configure Action<QuartzHostedServiceOptions>

A delegate that is used to configure an QuartzHostedServiceOptions.

Returns

IServiceCollection

Remarks

The options configured here apply to every scheduler, which is what they have always meant. One scheduler that has to differ says so with AddQuartzHostedService(IServiceCollection, string, Action<QuartzHostedServiceOptions>?).

AddQuartzHostedService(IServiceCollection, string, Action<QuartzHostedServiceOptions>?)

Configures how a named scheduler is started and stopped by the hosted service.

public static IServiceCollection AddQuartzHostedService(this IServiceCollection services, string schedulerName, Action<QuartzHostedServiceOptions>? configure = null)

Parameters

services IServiceCollection

The IServiceCollection.

schedulerName string

The name the scheduler was registered under with AddQuartz(name, …).

configure Action<QuartzHostedServiceOptions>

A delegate that is used to configure an QuartzHostedServiceOptions.

Returns

IServiceCollection

Remarks

The hosted service starts every scheduler in the container; this says how one of them is treated. A scheduler that should wait for application startup while another does not, or that needs longer to drain on shutdown, is configured here. It refines the settings shared by every scheduler, whichever order the two calls are made in.

AddQuartzHostedService<T>(IServiceCollection, Action<QuartzHostedServiceOptions>?)

Adds a hosted service of your own deriving from QuartzHostedService.

public static IServiceCollection AddQuartzHostedService<T>(this IServiceCollection services, Action<QuartzHostedServiceOptions>? configure = null) where T : QuartzHostedService

Parameters

services IServiceCollection

The IServiceCollection.

configure Action<QuartzHostedServiceOptions>

A delegate that is used to configure an QuartzHostedServiceOptions.

Returns

IServiceCollection

Type Parameters

T

Type extending the QuartzHostedService class.

Remarks

The options configured here apply to every scheduler, which is what they have always meant. One scheduler that has to differ says so with AddQuartzHostedService(IServiceCollection, string, Action<QuartzHostedServiceOptions>?).

AddQuartzSchedulers(IServiceCollection, IConfiguration, Action<IQuartzBuilder>?)

Registers one named Quartz scheduler per child of the section's Schedulers sub-section.

public static IServiceCollection AddQuartzSchedulers(this IServiceCollection services, IConfiguration configuration, Action<IQuartzBuilder>? configure = null)

Parameters

services IServiceCollection

The service collection to register into.

configuration IConfiguration

The Quartz configuration section, containing a Schedulers sub-section.

configure Action<IQuartzBuilder>

Applied to every scheduler described by the section.

Returns

IServiceCollection

Remarks

Each child's key is the scheduler's name, and its contents are that scheduler's configuration — exactly what AddQuartz(name, section) would be given. The fan-out is its own method because registering several schedulers is a different act from registering one, and reading it out of the shape of a configuration file made AddQuartz mean two things depending on data it was handed.

The section is a parameter because this receiver has no configuration of its own, which is the rule every method here follows: a service collection is handed its configuration and never goes looking for one, so AddQuartz(configure) means a scheduler configured entirely in code rather than one that quietly reads whatever IConfiguration the container holds. On a host application builder, which does hold configuration, this is builder.AddQuartzSchedulers(…) and takes no section — the same difference AddQuartz has between the two receivers.

ConfigureAllQuartzSchedulers(IServiceCollection, Action<IQuartzBuilder>)

Configures every Quartz scheduler in the container, whenever it was registered.

public static IServiceCollection ConfigureAllQuartzSchedulers(this IServiceCollection services, Action<IQuartzBuilder> configure)

Parameters

services IServiceCollection

The service collection to register into.

configure Action<IQuartzBuilder>

Applied to every scheduler in the container.

Returns

IServiceCollection

Remarks

This is the options pattern's ConfigureAll, for schedulers. configure is applied to every scheduler AddQuartz(), AddQuartz(name, …) or AddQuartzSchedulers(…) registers in this container: the ones already registered when this is called, and the ones registered after it. The order of the calls does not matter — which is the point, since a package that adds something to every scheduler cannot know whether the application registers its schedulers before or after calling it.

The delegate is given a builder per scheduler, so what it registers lands under that scheduler's own service key, exactly as if it had been written inside that scheduler's AddQuartz(name, q => …) callback. A plugin or listener added here is therefore one instance per scheduler, each initialized with the name of the scheduler it belongs to — not one instance shared between them, which would leave each scheduler but the last with a component pointing at somebody else's.

It runs after each scheduler's own configuration callback, which is what makes the order of the two calls immaterial: a scheduler registered later is configured then, and one registered earlier is configured here, and both are after its own callback either way. One consequence is worth naming: a middleware added here always composes inside one a scheduler's own AddQuartz callback added, because middleware runs in registration order and this registration is always the later of the two. That is the right way round — what a library wraps, an outbox or a unit of work, belongs within what the application wraps, such as its tenant scope. The usual precedence follows — registration is first-wins, so a component a scheduler chose for itself is not replaced by one chosen here; options are last-wins, so a value set here overrides the same option set on one scheduler, exactly as ConfigureAll<TOptions> overrides an earlier named Configure.

Remote schedulers registered with AddQuartzHttpClient are not built by a builder and are skipped. Calling this when no scheduler is registered at all is not an error: the delegate simply applies to nothing.