Quartz.Jobs 4.0.0-alpha.1

Prefix Reserved
This is a prerelease version of Quartz.Jobs.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Quartz.Jobs --version 4.0.0-alpha.1
                    
NuGet\Install-Package Quartz.Jobs -Version 4.0.0-alpha.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Quartz.Jobs" Version="4.0.0-alpha.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Quartz.Jobs" Version="4.0.0-alpha.1" />
                    
Directory.Packages.props
<PackageReference Include="Quartz.Jobs" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Quartz.Jobs --version 4.0.0-alpha.1
                    
#r "nuget: Quartz.Jobs, 4.0.0-alpha.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Quartz.Jobs@4.0.0-alpha.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Quartz.Jobs&version=4.0.0-alpha.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Quartz.Jobs&version=4.0.0-alpha.1&prerelease
                    
Install as a Cake Tool

title: Jobs

Quartz.Jobs provides some useful ready-made jobs for your convenience.

Quartz provides a number of utility jobs that you can use in your application for doing things like sending e-mails and invoking native processes. These out-of-the-box jobs live in the Quartz.Jobs namespace, which is also the assembly and NuGet package name. In 3.x the namespace was the singular Quartz.Job; a configuration string or a stored JOB_CLASS_NAME naming the old spelling still resolves, with a warning.

Installation

You need to add NuGet package reference to your project which uses Quartz.

dotnet add package Quartz.Jobs

How these jobs are configured

Each of these jobs reads its settings from its JobDataMap, under the keys listed with it below. Those keys are the persisted form: they are what a job store writes, what a cluster shares, and what an XML or JSON scheduling file names.

Each job also has an options type that maps onto exactly those keys, and an extension that writes it. It is the same stored job either way — but the key cannot be misspelled, the value cannot be of the wrong type, and every setting the job honours is a named property you can find by typing a dot.

Job Options Extension
DirectoryScanJob DirectoryScanOptions UsingDirectoryScanOptions(…)
FileScanJob FileScanOptions UsingFileScanOptions(…)
NativeJob NativeJobOptions UsingNativeJobOptions(…)
SendMailJob SendMailOptions UsingSendMailOptions(…)

The extensions work on both configuration surfaces — JobBuilder.Create<TJob>() and the configurator AddJob<TJob>(…) hands you — and each leaves you with what you started with, so the chain continues as usual. Options.FromJobData(map) reads the same settings back out of a job's data.

Features

DirectoryScanJob

Inspects a directory and compares whether any files' "last modified dates" have changed since the last time it was inspected. If one or more files have been updated, created or deleted, the job invokes a call-back method on an IDirectoryScanListener.

IJobDetail job = JobBuilder.Create<DirectoryScanJob>()
    .WithIdentity("inboxScan")
    .UsingDirectoryScanOptions(new DirectoryScanOptions
    {
        Directories = ["/var/spool/inbox"],
        ScanListenerName = nameof(InboxListener),
        SearchPattern = "*.csv",
        IncludeSubDirectories = true,
        MinimumUpdateAge = TimeSpan.FromSeconds(30),
    })
    .Build();
Setting Job data key Default
Directories DIRECTORY_NAMES (semicolon-separated), or DIRECTORY_NAME for one
DirectoryProviderName DIRECTORY_PROVIDER_NAME none; the paths above are used
ScanListenerName DIRECTORY_SCAN_LISTENER_NAME required
SearchPattern SEARCH_PATTERN *
IncludeSubDirectories INCLUDE_SUB_DIRECTORIES false
MinimumUpdateAge MINIMUM_UPDATE_AGE, in milliseconds 5 seconds

MinimumUpdateAge is how long a file must have been left alone before the job reports it. Without it a file another process is still writing would be handed to the listener half-finished.

The listener is found in one of two ways, in this order:

  1. Dependency injection (recommended): register your IDirectoryScanListener implementation in the container, and name its type — ScanListenerName = nameof(InboxListener).
  2. SchedulerContext: store the instance under a key, and name that key.
scheduler.Context["inboxListener"] = new InboxListener();

Where the directories come from can be decided at run time instead of being listed: implement IDirectoryProvider, put the instance in the SchedulerContext, and name that key as DirectoryProviderName. It is handed the merged job data and returns the paths to scan.

The job keeps its own bookkeeping — the last modification time it saw and the file list it saw it in — in the job detail's data map, which is why it is [PersistJobDataAfterExecution].

FileScanJob

Inspects a single file and compares whether its "last modified date" has changed since the last time it was inspected. If it has, the job invokes a call-back method on an IFileScanListener found in the SchedulerContext.

IJobDetail job = JobBuilder.Create<FileScanJob>()
    .WithIdentity("configWatch")
    .UsingFileScanOptions(new FileScanOptions
    {
        FileName = "/etc/app/settings.json",
        ScanListenerName = "settingsListener",
        MinimumUpdateAge = TimeSpan.FromSeconds(5),
    })
    .Build();
Setting Job data key Default
FileName FILE_NAME required
ScanListenerName FILE_SCAN_LISTENER_NAME required
MinimumUpdateAge MINIMUM_UPDATE_AGE, in milliseconds 5 seconds

NativeJob

Runs a native executable in a separate process.

IJobDetail job = JobBuilder.Create<NativeJob>()
    .WithIdentity("dumbJob")
    .UsingNativeJobOptions(new NativeJobOptions
    {
        Command = "echo",
        Parameters = "\"hi\" >> foobar.txt",
    })
    .Build();

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity("dumbTrigger")
    .WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromSeconds(5)).RepeatForever())
    .Build();

await scheduler.ScheduleJob(job, trigger);
Setting Job data key Default
Command command required
Parameters parameters none
WaitForProcess waitForProcess true
ConsumeStreams consumeStreams false
WorkingDirectory workingDirectory the scheduler's

When WaitForProcess is on, the integer exit code of the process is saved as the job execution result in the IJobExecutionContext. Turn ConsumeStreams on for a chatty process: one that writes more output than its pipe holds blocks until someone reads it.

SendMailJob

Sends an e-mail with the configured content to the configured recipient.

IJobDetail job = JobBuilder.Create<SendMailJob>()
    .WithIdentity("nightlyDigest")
    .UsingSendMailOptions(new SendMailOptions
    {
        SmtpHost = "smtp.example.com",
        SmtpPort = 587,
        Sender = "scheduler@example.com",
        Recipient = "ops@example.com",
        Subject = "Nightly digest",
        Message = "Everything ran.",
    })
    .Build();
Setting Job data key Default
SmtpHost smtp_host required
SmtpPort smtp_port the client's default
Sender sender required
Recipient recipient required
CcRecipient cc_recipient none
ReplyTo reply_to the sender
Subject subject required
Message message required
Encoding encoding the default

Override Send(MailInfo, CancellationToken) to route the mail through something other than SmtpClient, or BuildMessage(SendMailOptions) to add to the message — an attachment, a header — before it goes.

Keep the SMTP credential out of job data

SendMailOptions has no user name or password on purpose. Job data is durable: a persistent job store writes it to QRTZ_JOB_DETAILS, every node in the cluster reads it, the dashboard shows it, and any export of that table carries it. A password put there is a password in all of those places.

Register the credential with the container instead, and the job authenticates with it:

services.AddSingleton<ICredentialsByHost>(new NetworkCredential("mailer", smtpPassword));

ICredentialsByHost is what SmtpClient.Credentials takes, so a CredentialCache covers several servers. The password itself belongs wherever the rest of your secrets live — user secrets in development, a key vault or an environment variable in production — and reaches this registration through IConfiguration.

The smtp_username and smtp_password job data keys are still read when nothing is registered, so a job scheduled by an earlier version keeps sending. The job logs a warning when it uses them, and a credential from the container wins.

NoOpJob

A job that does nothing. Useful as a placeholder, and for triggering listeners on a schedule without any work attached.

Registering these jobs with the container

The jobs take their dependencies — a TimeProvider, an IServiceProvider, an ICredentialsByHost — from the container, so register them the same way you register your own:

builder.Services.AddQuartz(q =>
{
    q.AddJob<NativeJob>(j => j
        .WithIdentity("nightlyReport")
        .StoreDurably()
        .UsingNativeJobOptions(new NativeJobOptions
        {
            Command = "report.exe",
            Parameters = "--nightly",
            ConsumeStreams = true,
        }));

    q.AddTrigger<NativeJob>(t => t
        .ForJob("nightlyReport")
        .WithCronSchedule("0 0 2 * * ?"));
});
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (27)

Showing the top 5 NuGet packages that depend on Quartz.Jobs:

Package Downloads
Quartz.Plugins

Quartz.NET Plugins; Quartz Scheduling Framework for .NET

Castle.Facilities.Quartz

Castle Windsor Quartz facility lets you easily add windsor to Quartz apps.

Cogito.Quartz

Provides utilites and extensions for Quartz.

eV.Module.Job

eV.Module.Job

Hummingbird.Extensions.Quartz

Package Description

GitHub repositories (3)

Showing the top 3 popular GitHub repositories that depend on Quartz.Jobs:

Repository Stars
guoming/Hummingbird
分布式锁,分布式ID,分布式消息队列、配置中心、注册中心、服务注册发现、超时、重试、熔断、负载均衡
yc-l/yc.boilerplate
YC. Boilerplate is a set of loose coupling, flexible combination, complete functions, convenient development, and reduces the workload of development.
microsoft/Recurring-Integrations-Scheduler
Recurring Integrations Scheduler (RIS) is a solution that can be used in file-based integration scenarios for Dynamics 365 Finance and Dynamics 365 Supply Chain Management.
Version Downloads Last Updated
4.0.0-alpha.4 0 8/31/2026
4.0.0-alpha.3 48 8/27/2026
4.0.0-alpha.2 49 8/25/2026
4.0.0-alpha.1 78 8/22/2026
3.20.0 740 8/27/2026
3.19.1 25,256 7/26/2026
3.19.0 9,287 7/24/2026
3.18.2 37,849 6/27/2026
3.18.1 130,683 4/25/2026
3.18.0 50,032 4/11/2026
3.17.1 13,859 4/3/2026
3.17.0 8,658 3/29/2026
3.16.1 61,579 3/4/2026
3.16.0 5,343 3/1/2026
3.15.1 214,575 10/26/2025
3.15.0 134,684 8/3/2025
3.14.0 459,011 3/8/2025
3.13.1 537,477 11/2/2024
3.13.0 232,927 8/10/2024
3.12.0 29,200 8/3/2024
Loading failed