Schedule / ScheduleAsync
Schedules a single train to run on a recurring basis. Schedule is used at startup configuration time; ScheduleAsync is used at runtime via ITraxScheduler.
Both use upsert semantics — if a manifest with the given externalId already exists, it is updated; otherwise a new one is created.
Signatures
Startup (SchedulerConfigurationBuilder) — Recommended
The input type TInput is inferred from the TTrain interface at configuration time — only a single type parameter is needed:
public SchedulerConfigurationBuilder Schedule<TTrain>(
string externalId,
IManifestProperties input,
Schedule schedule,
Action<ScheduleOptions>? options = null
)
where TTrain : classThe method resolves TInput by reflecting on TTrain's IServiceTrain<TInput, TOutput> interface. If the provided input doesn't match the expected type, an InvalidOperationException is thrown at configuration time. The output type is not constrained — scheduled trains can return any output type, and the output is discarded for background jobs.
Startup (SchedulerConfigurationBuilder) — Explicit Type Parameters
The legacy three-type-parameter form is still available for backward compatibility:
public SchedulerConfigurationBuilder Schedule<TTrain, TInput, TOutput>(
string externalId,
TInput input,
Schedule schedule,
Action<ScheduleOptions>? options = null
)
where TTrain : IServiceTrain<TInput, TOutput>
where TInput : IManifestPropertiesRuntime (ITraxScheduler)
Task<Manifest> ScheduleAsync<TTrain, TInput, TOutput>(
string externalId,
TInput input,
Schedule schedule,
Action<ScheduleOptions>? options = null,
CancellationToken ct = default
)
where TTrain : IServiceTrain<TInput, TOutput>
where TInput : IManifestPropertiesType Parameters
| Type Parameter | Constraint | Description |
|---|---|---|
TTrain | class (inferred) / IServiceTrain<TInput, TOutput> (explicit) | The train interface type. Can implement IServiceTrain<TInput, TOutput> with any output type. The scheduler resolves the concrete implementation via TrainBus using the input type. The output is discarded for background jobs. |
TInput | IManifestProperties | Inferred at startup from TTrain's interface. The input type for the train. Must implement IManifestProperties (a marker interface) to enable serialization for scheduled job storage. Only required explicitly in the explicit three-type-param form and the runtime API. |
TOutput | — | The output type of the train. Not constrained — any output type is accepted. The output is discarded when the job completes. Only required explicitly in the explicit three-type-param form and the runtime API. |
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
externalId | string | Yes | — | A unique identifier for this scheduled job. Used for upsert semantics — if a manifest with this ID exists, it will be updated; otherwise a new one is created. Also used to reference this job in dependent scheduling (ThenInclude, Include). |
input | IManifestProperties (inferred) / TInput (explicit) | Yes | — | The input data that will be passed to the train on each execution. Serialized and stored in the manifest. With the inferred API, the concrete type is validated against the train's expected input type at configuration time. |
schedule | Schedule | Yes | — | The schedule definition — either interval-based or cron-based. Cron schedules support both 5-field (minute) and 6-field (second) granularity. Use Every or Cron helpers to create one. Supports .WithVariance(TimeSpan) to add random jitter. |
options | Action<ScheduleOptions>? | No | null | Optional callback to configure all scheduling options via a fluent builder. Includes manifest-level settings (Priority, Enabled, MaxRetries, Timeout), group-level settings (.Group(...) with MaxActiveJobs, Priority, Enabled), and batch settings (PrunePrefix). See ScheduleOptions below. |
ct | CancellationToken | No | default | Cancellation token (runtime API only). |
ScheduleOptions
The ScheduleOptions fluent builder consolidates all optional scheduling parameters:
Manifest-level methods
| Method | Description |
|---|---|
.Priority(int) | Dispatch priority (0-31). Higher values dispatched first. |
.Enabled(bool) | Whether the manifest is enabled. Default: true. |
.MaxRetries(int) | Max retry attempts before dead-lettering. Default: 3. |
.Timeout(TimeSpan) | Job execution timeout. null uses global default. |
.OnMisfire(MisfirePolicy) | Misfire policy for missed runs. FireOnceNow (default) fires immediately; DoNothing skips and waits for the next natural occurrence. Only applies to Cron and Interval types. |
.MisfireThreshold(TimeSpan) | Grace period before the misfire policy takes effect. Overrides the global DefaultMisfireThreshold. |
.Variance(TimeSpan) | Adds random jitter to the schedule. After each successful run, the next execution is delayed by [0, variance] seconds. Only supported on Interval and Cron types. If Schedule.WithVariance() is also set, the schedule-level value takes precedence. See Schedule Variance. |
.Exclude(Exclusion) | Adds an exclusion window. The manifest is skipped when any exclusion matches the current time. Multiple can be combined. Use Exclude.DaysOfWeek(...), Exclude.Dates(...), Exclude.DateRange(...), or Exclude.TimeWindow(...) factories. See Exclusion Windows. |
Group-level methods
| Method | Description |
|---|---|
.Group(string groupId) | Sets the manifest group name. Defaults to externalId when not set. |
.Group(string groupId, Action<ManifestGroupOptions>) | Sets group name and configures group dispatch settings. |
.Group(Action<ManifestGroupOptions>) | Configures group dispatch settings without changing the group name. |
ManifestGroupOptions
| Method | Description |
|---|---|
.MaxActiveJobs(int?) | Max concurrent active jobs for this group. null = no per-group limit. |
.Priority(int) | Group dispatch priority (0-31). Defaults to manifest priority if not set. |
.Enabled(bool) | Kill switch for the entire group. Default: true. |
Returns
- Startup:
SchedulerConfigurationBuilder— for continued fluent chaining. - Runtime:
Task<Manifest>— the created or updated manifest record.
Examples
Startup Configuration (Recommended — Inferred Input Type)
services.AddTrax(trax => trax
.AddScheduler(scheduler => scheduler
.Schedule<ISyncTrain>(
"sync-daily",
new SyncInput { Source = "production" },
Cron.Daily(hour: 3),
options => options
.MaxRetries(5)
.Priority(20)
.Group("daily-syncs"))
)
);Only the train interface type is specified. The input type (SyncInput) is inferred from ISyncTrain : IServiceTrain<SyncInput, TOutput> and validated at configuration time. The output type is not constrained — it can be Unit or any other type, and the output is discarded for background jobs.
Runtime Scheduling
public class MyService(ITraxScheduler scheduler)
{
public async Task SetupSchedule()
{
var manifest = await scheduler.ScheduleAsync<ISyncTrain, SyncInput, Unit>(
"sync-on-demand",
new SyncInput { Source = "staging" },
Every.Hours(1),
options => options.Priority(15));
}
}Schedule Record
The Schedule record defines the timing for a scheduled manifest. Create instances via Every.*, Cron.*, or Schedule.FromInterval() / Schedule.FromCron().
Properties
| Property | Type | Description |
|---|---|---|
Type | ScheduleType | Interval or Cron. |
Interval | TimeSpan? | The interval between runs (Interval type only). |
CronExpression | string? | The cron expression (Cron type only). |
Variance | TimeSpan? | Optional random jitter added after each successful run. |
Methods
| Method | Returns | Description |
|---|---|---|
WithVariance(TimeSpan variance) | Schedule | Returns a new Schedule with the specified variance. The next run after each success is delayed by a random [0, variance] duration. Only valid on Interval and Cron types — applying it to other types throws InvalidOperationException at manifest creation time. |
// Interval with 2-minute jitter
var schedule = Every.Minutes(5).WithVariance(TimeSpan.FromMinutes(2));
// Cron with 30-minute jitter
var schedule = Cron.Daily(3).WithVariance(TimeSpan.FromMinutes(30));Remarks
- At startup, manifests are not created immediately — they are captured and seeded when the application starts via
SchedulerStartupService. - At runtime,
ScheduleAsynccreates/updates the manifest in the database immediately. - The
externalIdis the primary key for upsert logic. Changing it creates a new manifest rather than updating the existing one. - If the train type is not registered in the
TrainRegistry(viaAddMediator), anInvalidOperationExceptionis thrown. - The group is determined by
.Group(...)onScheduleOptions. When not specified, it defaults to theexternalId. Groups are auto-created (upserted by name) during scheduling. Orphaned groups are cleaned up on startup. - For one-off jobs that should run once and auto-disable, use ScheduleOnceAsync instead of
ScheduleAsync. It creates a manifest withScheduleType.Once— noScheduleobject needed. See Delayed / One-Off Jobs for details.