Jobs with parameters

In some situations it's useful for a job to take parameters which is just a data bag coming from outside of the job run. Some example use cases:

  • A job that fetches data from an external system based on change date can include a DateTime in it's parameters and pass a date to the next job execution. This allows the future run to only fetch changes that happened after the previous run.
  • Since parameters can be passed explicitly from the UI when starting a job you can define parameter settings that allows the manual run to affect how the job is executed.
  • There might be side effects that you typically don't want to do in a job such as writing a large file to disk to help troubleshooting. And instead of having a config value for this you can use parameters to only save the file to disk when you explicitly want to.

You define a scheduled job that takes parameters by implementing the interface IScheduledJob<TParameters>. Here's an example implementation:

public class MyJobParameters
{
    public DateTime LastRunDate { get; set;}
}

public class MyJobWithParameters : IScheduledJob<MyJobParameters>
{
    public string DefaultSchedule => CronSchedule.TimesPerMinute(10);

    public MyJobParameters DefaultParameters => new MyJobParameters();

    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 (MyJobParameters in the example) can be any class that can be serialized to and from JSON. If you wish to customize how parameters are serialized and deserialized you can register your own implementation of the interface IJobParametersSerializer in the IServiceCollection.

In the above example you see how one run of a job can pass parameters to the next run but this is not required. If you don't pass any parameters to the next run, and there's no manual parameters passed when starting a job through the UI your job will get passed the default parameters.

Parameters and job failure

If a job run with parameters fails the next run of the job will receive the same parameters as the run that failed. So in the above example the LastRunDate would contain the date set by the last successful run.

If you don't want this behavior you can catch errors yourself and pass explicit parameters to JobResults.Failed() like this:

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 will still get marked as failed but the next run will receive the parameters passed to JobResults.Failed().

DefaultParameters

This property should return an instance of the parameters class which will be passed to ExecuteAsync() if we don't have any other parameters to pass. For example when running the job for the very first time or if the previous job run didn't pass any parameters to the next run.