Jobs with parameters
In some situations it is useful for a job to take parameters: a data bag supplied from outside the job run. Some example use cases are:
- A job that fetches data from an external system based on a change date can include a
DateTimein its parameters and pass a date to the next execution. The future run can then fetch only changes that happened after the previous run. - Parameters can be supplied explicitly when starting a job from the Admin UI, allowing a manual run to affect how the job is executed.
- An occasionally useful side effect, such as writing detailed troubleshooting output, can be controlled for an individual run instead of through application configuration.
- A user can upload a file that the job reads during its execution.
Defining a parameterized job
Define a scheduled job that takes parameters by implementing IScheduledJob<TParameters>. For example:
public class MyJobParameters
{
public DateTime LastRunDate { get; set; }
}
public class MyJobWithParameters : IScheduledJob<MyJobParameters>
{
public string DefaultSchedule => CronSchedule.TimesPerMinute(10);
public MyJobParameters DefaultParameters => new();
public async Task<JobResults> ExecuteAsync(MyJobParameters parameters, CancellationToken cancellationToken)
{
var now = DateTime.UtcNow;
var changes = await _service.GetChangesAsync(from: parameters.LastRunDate, to: now);
// Do something interesting with the changes
return JobResults.Completed($"Processed {changes.Count} changes", new MyJobParameters
{
LastRunDate = now,
});
}
}
The parameters class can be any class that can be serialized to and from JSON. Nexus stores default, next-run, retry, and manually supplied parameters as JSON. No separate database schema is created for a parameters class.
To customize parameter serialization, register an implementation of IJobParametersSerializer in the dependency injection container.
Editing parameters in the Admin UI
When Nexus can describe a job's parameter properties, the Admin UI offers two modes:
- Fields is selected initially and renders controls appropriate for each property.
- JSON provides direct access to the complete parameters object for technical users.
The same editor is used when starting a job and when editing its default or next-run parameters on the job details page. The start-job editor is initialized from the saved default parameters, falling back to the job's parameter example when no defaults are available.
Changes are preserved when switching modes. Switching from JSON to Fields requires a valid JSON object. Fields are validated before switching to JSON or submitting the form. File selection is available only in Fields mode.
Supported fields
Nexus describes public properties that have both a public getter and public setter. Read-only, indexed, and JSON-ignored properties are omitted. Objects, collections, and scalar types without a dedicated control are represented by an individual JSON editor rather than being recursively expanded.
The JSON tab always shows the serialized representation and does not perform timezone conversion or provide file pickers.
Labels, descriptions, and serialized names
By default Nexus creates a label from the property name. Attributes can provide a better label, help text, or serialized property name:
public class ImportParameters
{
[DisplayName("Changes from")]
[Description("Only import changes made on or after this date.")]
[JsonPropertyName("fromDate")]
public DateOnly From { get; set; }
}
DisplayNameAttribute and DisplayAttribute control the label, while DescriptionAttribute adds help text. JsonPropertyNameAttribute, Newtonsoft.Json's JsonPropertyAttribute, and the configured System.Text.Json property naming policy are respected when available. Both System.Text.Json and Newtonsoft.Json ignore attributes are respected.
The API exposes this metadata in JobSummary.ParameterFields. JobSummary.FileUploadsEnabled tells clients whether file controls can currently accept uploads.
Default parameters
DefaultParameters should return an instance of the parameters class. It is passed to ExecuteAsync() when Nexus has no other parameters to use, such as for the first run or when the previous run did not provide parameters for the next run.
Default parameters can be edited in either Fields or JSON mode on the job details page. Values saved in the Admin UI override the DefaultParameters property on the job class. To remove the saved override and use the value from code again, use the Reset action on the job details page.
Next-run parameters
A job can pass parameters to its next execution by returning them in its JobResults, as shown in the first example. This is optional. The next-run parameters can also be edited in Fields or JSON mode on the job details page.
If neither a previous run nor a manual start supplies parameters, Nexus uses the default parameters.
Parameters and job failure
If a non-manual job run with parameters fails, its next run receives the same parameters. In the first example, LastRunDate therefore contains the date set by the last successful run.
To use different parameters after a failure, catch the error and pass explicit parameters to JobResults.Failed():
public async Task<JobResults> ExecuteAsync(MyJobParameters parameters, CancellationToken cancellationToken)
{
var now = DateTime.UtcNow;
try
{
// Implementation excluded for brevity
}
catch (Exception e)
{
return JobResults.Failed(e, new MyJobParameters
{
LastRunDate = now,
});
}
}
The run is still marked as failed, but the next run receives the parameters supplied to JobResults.Failed().
Uploaded files
Use JobFile for a file parameter:
public class ImportParameters
{
[Description("The CSV file to import.")]
public JobFile InputFile { get; set; } = null!;
}
public class ImportJob : IScheduledJob<ImportParameters>
{
public string DefaultSchedule => CronSchedule.NotScheduled();
public ImportParameters DefaultParameters => new();
public async Task<JobResults> ExecuteAsync(ImportParameters parameters, CancellationToken cancellationToken)
{
var contents = parameters.InputFile.ReadAsText();
// Import the contents
return JobResults.Completed();
}
}
Configuring filesystem storage
File uploads are disabled by default. Set UploadedFilesPath when registering scheduled jobs to enable the built-in filesystem storage:
builder.Services
.AddNexus()
.AddScheduledJobs(options =>
{
options.UploadedFilesPath = @"\\fileserver\shared\nexus-job-files";
options.UploadedFileRetention = TimeSpan.FromDays(14);
options.UploadedFileCleanupInterval = TimeSpan.FromHours(1);
});
Use a shared folder when a job can be uploaded on one application instance and executed on another. The built-in storage uses generated opaque filenames and never uses the uploaded filename as a filesystem path. Storage keys are validated before paths are resolved.
UploadedFileRetention defaults to seven days, and UploadedFileCleanupInterval defaults to one hour. A hosted cleanup loop sweeps once at startup and then at the configured interval. It also removes uploads abandoned when a user selects a file but never successfully starts or saves the job parameters.
Uploaded references can be reused by retries and later runs only until cleanup removes the underlying file. Configure retention to be longer than the maximum expected delay before a run, including retries. Be especially careful when placing uploaded references in default or next-run parameters, because those references do not make the file permanent.
When uploads are disabled, the Admin UI keeps file controls disabled and explains that UploadedFilesPath or a custom storage implementation is required.
Using custom file storage
Implement IJobFileStorage to store files somewhere else, such as Azure Blob Storage:
IsEnabledreports whether the storage is available for uploads.UploadAsync()stores the supplied stream and returns aJobFilecontaining a persistable opaque reference.OpenReadAsync()reconstructs a readable stream from that reference before job execution.DeleteOlderThanAsync()deletes storage objects older than the supplied UTC cutoff.
Register the custom implementation before adding scheduled jobs so it replaces the default registration:
builder.Services.AddSingleton<IJobFileStorage, AzureBlobJobFileStorage>();
builder.Services
.AddNexus()
.AddScheduledJobs(options =>
{
options.UploadedFileRetention = TimeSpan.FromDays(30);
});
UploadedFilesPath is not required for custom storage. The same retention and cleanup interval options control calls to the custom implementation's DeleteOlderThanAsync() method.