Table of Contents

Interface IPersistentStoreBuilder

Namespace
Quartz
Assembly
Quartz.dll

Configures a database-backed job store.

public interface IPersistentStoreBuilder
Extension Methods

Remarks

A scheduler has one job store and therefore one database, so there is no data source name to invent: UseSqlServer(connectionString) says everything. Schedulers that need different databases are registered under different names, and their components are keyed accordingly.

Serialization and clustering live here rather than on the scheduler because they are properties of how the schedule is stored, and mean nothing for an in-memory store.

Properties

SchedulerName

The name of the scheduler this store belongs to, or an empty string for the default scheduler.

string SchedulerName { get; }

Property Value

string

Services

The services this scheduler is built from.

IServiceCollection Services { get; }

Property Value

IServiceCollection

Methods

ConfigureStore(Action<AdoJobStoreOptions>)

Configures the job store itself.

IPersistentStoreBuilder ConfigureStore(Action<AdoJobStoreOptions> configure)

Parameters

configure Action<AdoJobStoreOptions>

Returns

IPersistentStoreBuilder

Remarks

These are the same settings, under the same names, as the Quartz:JobStore configuration section. The name says which of the several things in scope here is being configured — the store, rather than the scheduler around it or the data source under it.

ProvisionSchema()

Creates whatever this store's schema is missing when the scheduler starts, instead of requiring it to be there already.

IPersistentStoreBuilder ProvisionSchema()

Returns

IPersistentStoreBuilder

Remarks

Shorthand for ConfigureStore(options => options.SchemaProvisioning = SchemaProvisioning.CreateIfMissing), spelled as the decision it is.

Only ever creates. Nothing is altered and nothing is dropped, so this cannot turn a mis-typed TablePrefix into data loss — but it will happily create a second, empty schema under the mis-typed one, so the prefix is still worth reading twice. Neither is it an upgrade: a schema that has every table but is missing a column a later release added is left as it is. database/migrations/ is what moves a schema forward.

The account the store connects with needs permission to create tables and indexes, which a production database is usually right not to grant, and which is why this is opt-in rather than the default. Several schedulers may call it against one database at once: whichever loses the race finds the schema the winner created.

UseAmbientTransactions()

Runs the schedule inside a transaction somebody else owns — an application server or another framework that manages transactions — instead of one the store begins and commits itself.

IPersistentStoreBuilder UseAmbientTransactions()

Returns

IPersistentStoreBuilder

Remarks

This is how the second of the two shipped ADO.NET stores is chosen. Left uncalled, UsePersistentStore builds the local-transaction store, which begins the transaction each operation runs in and commits or rolls it back; the ambient store does neither, because the connections it creates enlist in the Transaction already in progress and its owner is what decides whether the scheduler's work is kept. It is quartz.jobStore.type = Quartz.Impl.AdoJobStore.ExternalTransactionJobStore, Quartz spelled in code — 3.x's JobStoreCMT.

Not the same thing as AcceptEnlistedTransactions, which leaves the local store in charge and only lets an operation join a transaction the application enlisted for that flow, managing its own whenever nothing is enlisted. This hands the whole store over.

The store requires database locking — an in-process lock would be released before its owner commits — and turns it on for itself unless UseLockHandler<T>() supplied one. OpenConnection, which nothing else reads, says whether the store opens the connections it creates or leaves that to the transaction's owner.

UseClustering(Action<ClusteringOptions>?)

Takes part in a cluster with every other scheduler sharing this database.

IPersistentStoreBuilder UseClustering(Action<ClusteringOptions>? configure = null)

Parameters

configure Action<ClusteringOptions>

Returns

IPersistentStoreBuilder

Remarks

Clustering requires database locking, which this enables as well — the two have never been separable in practice.

Everything about clustering is said here, on ClusteringOptions. The job store has no clustering settings of its own to disagree with these, and reports whether it is clustered rather than offering a second place to say so.

UseConnectionProvider(Func<IServiceProvider, IDbProvider>)

Uses a connection provider the caller builds, for cases where it needs configuring first.

IPersistentStoreBuilder UseConnectionProvider(Func<IServiceProvider, IDbProvider> factory)

Parameters

factory Func<IServiceProvider, IDbProvider>

Returns

IPersistentStoreBuilder

Remarks

This is the code spelling of 3.x's quartz.dataSource.<name>.connectionProvider.type, and of DBConnectionManager.AddConnectionProvider before it. Reach for it when connections have to come from somewhere Quartz cannot describe — a pooled or credential-rotating factory, or a driver whose connections need setting up after they are created.

Unlike the rest of this builder, this replaces rather than defers: it wins over the provider UseSqlServer and its siblings register, in either order, so there is no call sequence to get right. It also names this store's data source, so a store configured this way needs no UseDataSource(Action<DataSourceOptions>) call — though one is still useful for the driver delegate the database-specific methods select.

The provider belongs to this scheduler alone. Registering IDbProvider against Services instead would be invisible to a named scheduler, which resolves its provider under its own key.

UseConnectionProvider<T>()

Uses a connection provider of your own, which decides how connections and commands are made rather than leaving that to a connection string and a driver description.

IPersistentStoreBuilder UseConnectionProvider<T>() where T : class, IDbProvider

Returns

IPersistentStoreBuilder

Type Parameters

T

Remarks

This is the code spelling of 3.x's quartz.dataSource.<name>.connectionProvider.type, and of DBConnectionManager.AddConnectionProvider before it. Reach for it when connections have to come from somewhere Quartz cannot describe — a pooled or credential-rotating factory, or a driver whose connections need setting up after they are created.

Unlike the rest of this builder, this replaces rather than defers: it wins over the provider UseSqlServer and its siblings register, in either order, so there is no call sequence to get right. It also names this store's data source, so a store configured this way needs no UseDataSource(Action<DataSourceOptions>) call — though one is still useful for the driver delegate the database-specific methods select.

The provider belongs to this scheduler alone. Registering IDbProvider against Services instead would be invisible to a named scheduler, which resolves its provider under its own key.

UseDataSource(Action<DataSourceOptions>)

Defines this store's data source: which ADO.NET driver, and how to reach the database.

IPersistentStoreBuilder UseDataSource(Action<DataSourceOptions> configure)

Parameters

configure Action<DataSourceOptions>

Returns

IPersistentStoreBuilder

Remarks

Prefer the database-specific methods such as UseSqlServer, which also select the matching driver delegate. Use this directly only for a provider Quartz does not know about.

Where the connection itself comes from is DataSourceOptions' to say: a connection string, the name of one in IConfiguration, or UseRegisteredDataSource for a DbDataSource the application registered in the container.

UseDataSource(string)

Says which data source this store reads and writes through, by name.

IPersistentStoreBuilder UseDataSource(string name)

Parameters

name string

Returns

IPersistentStoreBuilder

Remarks

This refers to a data source rather than defining one: UseDataSource(Action<DataSourceOptions>) defines it, and the two are told apart by what they are given rather than by carrying different names. The settings it names are the DataSourceOptions registered under this name — from a Quartz:DataSource:<name> configuration section, say, or from another scheduler that already configured it.

The name defaults to the scheduler's name, or quartz for the default scheduler, so it never has to be invented or kept in step by hand. Naming one explicitly is for the cases where the name itself matters: two stores that should read the same Quartz:DataSource:<name> settings, or settings that live under a name the application chose.

Call this before choosing the database, since the name is fixed when the data source is configured.

UseDriverDelegate(Func<IServiceProvider, IDriverDelegate>)

Uses a driver delegate the caller builds, for cases where it needs configuring first.

IPersistentStoreBuilder UseDriverDelegate(Func<IServiceProvider, IDriverDelegate> factory)

Parameters

factory Func<IServiceProvider, IDriverDelegate>

Returns

IPersistentStoreBuilder

Remarks

As with UseSerializer(Func<IServiceProvider, IObjectSerializer>), this registers under the scheduler's own key, which registering against Services would not.

UseDriverDelegate<T>()

Uses a specific driver delegate, which adapts Quartz's SQL to a particular database.

IPersistentStoreBuilder UseDriverDelegate<T>() where T : class, IDriverDelegate

Returns

IPersistentStoreBuilder

Type Parameters

T

UseLockHandler(Func<IServiceProvider, ILockHandler>)

Uses a lock handler the caller builds, for cases where it needs configuring first.

IPersistentStoreBuilder UseLockHandler(Func<IServiceProvider, ILockHandler> factory)

Parameters

factory Func<IServiceProvider, ILockHandler>

Returns

IPersistentStoreBuilder

Remarks

As with UseSerializer(Func<IServiceProvider, IObjectSerializer>), this registers under the scheduler's own key, which registering against Services would not.

UseLockHandler<T>()

Uses a specific lock handler, which decides how competing schedulers serialize their work.

IPersistentStoreBuilder UseLockHandler<T>() where T : class, ILockHandler

Returns

IPersistentStoreBuilder

Type Parameters

T

Remarks

Left unset, the store chooses for itself once it knows which database it is talking to: database row locks when clustered, and an in-process monitor otherwise.

UseSerializer(Func<IServiceProvider, IObjectSerializer>)

Uses a serializer the caller builds, for cases where it needs configuring first.

IPersistentStoreBuilder UseSerializer(Func<IServiceProvider, IObjectSerializer> factory)

Parameters

factory Func<IServiceProvider, IObjectSerializer>

Returns

IPersistentStoreBuilder

Remarks

The serializer belongs to this store rather than to the container, so use this rather than registering IObjectSerializer against Services: a named scheduler resolves its serializer under its own key and would never see an unkeyed registration.

UseSerializer<T>()

Uses a specific serializer for job data held in the database.

IPersistentStoreBuilder UseSerializer<T>() where T : class, IObjectSerializer

Returns

IPersistentStoreBuilder

Type Parameters

T

UseTriggerPersistenceDelegate(Func<IServiceProvider, ITriggerPersistenceDelegate>)

Adds a trigger persistence delegate the caller builds, for cases where it needs configuring first.

IPersistentStoreBuilder UseTriggerPersistenceDelegate(Func<IServiceProvider, ITriggerPersistenceDelegate> factory)

Parameters

factory Func<IServiceProvider, ITriggerPersistenceDelegate>

Returns

IPersistentStoreBuilder

Remarks

As with UseSerializer(Func<IServiceProvider, IObjectSerializer>), this registers under the scheduler's own key, which registering against Services would not.

UseTriggerPersistenceDelegate<T>()

Adds a trigger persistence delegate, which stores and rebuilds a custom trigger type's scheduling data in its own tables rather than as a serialized blob.

IPersistentStoreBuilder UseTriggerPersistenceDelegate<T>() where T : class, ITriggerPersistenceDelegate

Returns

IPersistentStoreBuilder

Type Parameters

T

Remarks

The built-in delegates for the five shipped trigger types are always present; delegates added here serve additional trigger types. Call once per delegate — repeated registrations of the same type collapse to one.