Skip to content

Jobs Walkthrough

Jobs represent long-running or queued operations: model runs, batch exports, scheduled tasks. This page covers setup from package installation to first Swagger call.


The Job Entity

Job<TJobId, TTaskId> is the core entity. The default type parameters are Job<Guid, string>.

Key properties:

Property Type Description
Id Guid Unique job identifier (auto-generated)
TaskId string Which task definition to run (maps to a workflow or executor)
AccountId string The user/account that submitted the job
Status JobStatus Current lifecycle status (see below)
Progress int Completion percentage (0–100)
Priority int Scheduling priority — higher values run first
HostGroup string Tag that routes the job to a specific executor group
Tag string Arbitrary label for filtering/grouping
Parameters Dictionary<string,string> Key/value input parameters passed to the task
Requested / Started / Finished DateTime? Lifecycle timestamps

JobStatus Lifecycle

Pending → Starting → InProgress → Completed
                              ↘ Error
                              ↘ Cancelled  (Cancel → Cancelling → Cancelled)
                              ↘ TimedOut   (TimingOut → TimedOut)
                              ↘ CompletedUnsuccessfully

Full enum values:

Value Meaning
Pending Submitted, waiting to be picked up by an executor
Starting Executor has claimed the job and is starting it
InProgress Actively running
Completed Finished successfully
Error Finished with an unhandled error
Unknown Status cannot be determined
Cancel Cancellation requested by the user
Cancelling Executor is in the process of stopping the job
Cancelled Job was successfully cancelled
TimingOut Execution has exceeded the time limit
TimedOut Job was stopped due to timeout
CompletedUnsuccessfully Ran to completion but reported a failure result
Removed Job record has been marked for removal

Step 1 — Install the Package

dotnet add package DHI.Services.Jobs.WebApi

Step 2 — Register the Converters

In Program.cs, inside .AddJsonOptions(...):

options.JsonSerializerOptions.AddConverters(
    DHI.Services.Jobs.WebApi.SerializerOptionsDefault.Options.Converters
);

Step 3 — Register the Service

Option A — Declarative (connections.json)

Add an entry in App_Data/connections.json:

{
  "$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[DHI.Services.IConnection, DHI.Services]], mscorlib",
  "wf-jobs": {
    "$type": "DHI.Services.Jobs.WebApi.JobServiceConnection, DHI.Services.Jobs.WebApi",
    "ConnectionString": "[AppData]jobs.json",
    "RepositoryType": "DHI.Services.Jobs.JobRepository, DHI.Services.Jobs",
    "Name": "Jobs (JSON)",
    "Id": "wf-jobs"
  }
}

Enable the connections repository in Program.cs:

Services.Configure(
    new ConnectionRepository("[AppData]connections.json".Resolve()),
    lazyCreation: true
);

Option B — Imperative (code)

Register directly in Program.cs:

ServiceLocator.Register(
    new JobService<Workflow, string>(
        new JobRepository("[AppData]jobs.json".Resolve()),
        new TaskService<Workflow, string>(
            new WorkflowRepository(
                "[AppData]workflows.json".Resolve(),
                SerializerOptionsDefault.Options.Converters
            )
        ),
        null
    ),
    "wf-jobs"
);

The third argument to JobService is an optional IDiscreteService<Account, string> for account validation. Pass null to skip account validation during development.


Step 4 — SignalR Prerequisites

Jobs endpoints use SignalR for real-time status notifications. Register the dependencies in Program.cs:

builder.Services.AddSignalR();

builder.Services.AddSingleton<IFilterRepository>(
    new FilterRepository("[AppData]signalr-filters.json".Resolve())
);

builder.Services.AddScoped<Microsoft.Extensions.Logging.ILogger>(_ =>
    new SimpleLogger("[AppData]jobs.log".Resolve())
);

Note: signalr-filters.json and jobs.log do not need to exist — these registrations satisfy DI. The files are created automatically when data is first written.


Step 5 — Add Sample Data Files

Create App_Data/jobs.json with an empty dictionary to start:

{
  "$type": "System.Collections.Generic.Dictionary`2[[System.Guid, System.Private.CoreLib],[DHI.Services.Jobs.Job`2[[System.Guid, System.Private.CoreLib],[System.String, System.Private.CoreLib]], DHI.Services.Jobs]], System.Private.CoreLib"
}

Step 6 — Test in Swagger

Start the API and open Swagger. You should see /api/jobs/... endpoints.

Submit a new job:

POST /api/jobs/wf-jobs

Request body:

{
  "TaskId": "WriteToFile",
  "Priority": 1,
  "HostGroup": "Minion",
  "Tag": "demo"
}

Get all jobs:

GET /api/jobs/wf-jobs

Get a specific job by ID:

GET /api/jobs/wf-jobs/{jobId}

The new job starts with Status: "Pending". An executor (Job Runner or Orchestrator) must pick it up to advance the status.


Next Steps: Executors and Automation

This walkthrough sets up the Jobs API (storage and status management). To actually run jobs you need an executor:

Component NuGet package Role
Job Runner DHI.Services.Jobs.Executer Polls for pending jobs and executes them in-process
Orchestrator DHI.Services.Jobs.Orchestrator Manages multiple executors and load balancing
Workflow Worker DHI.Services.Jobs.WorkflowWorker Executes Windows Workflow Foundation (WF) activities

See domain_services/references/jobs/ for documentation on each executor component.


Next: GIS Walkthrough