Job context
Read facts about the current run with IJobContext, log with them, and wrap runs in middleware.
On this page
IJobContext gives a handler read-only facts about the run it is in: the job ID, which run this is, the queue, metadata, schedule and more. Inject it into the handler's constructor. It is not a HandleAsync argument, so handlers that do not need it simply leave it out.
There is one context per run. It can only be resolved inside the run's DI scope; resolving it anywhere else throws InvalidOperationException.
What it exposes
Identity
Runs and budget
ExecutionNumber == MaxExecutions means this is the last automatic try. You might log louder, or send a different alert, on that run.
Routing and payload
Metadata, tracing and fences
Everything except Items and CancellationCauses stays the same for the whole run.
Scheduled jobs
For a job created by a schedule, Schedule holds:
What it does not expose
The context is read-only by design. It has no database connection, no lease, no way to extend the lease or the timeout, and no progress reporting. Lease renewal is done by the worker, not the handler, so a handler cannot keep a job alive forever.
If you need to report progress, write it to your own table or cache keyed by JobId.
Logging
Runwick does not add a logging scope for you. Inject ILogger<T> as usual and include the IDs you need:
When tracing is on, each run gets a process {queue} span, and the handler runs inside a runwick.execute child span. Spans your own code starts become its children, and the run is linked to the dispatch that created it. See Observability.
Middleware
Middleware wraps every run, like ASP.NET Core middleware wraps a request. It receives the same context and token as the handler:
A middleware either calls next() exactly once and returns its result, or returns a result without calling it. Calling next() twice, or returning while it is still running, is a contract fault and fails the job permanently.
Register middleware globally, per job, or by alias so that a contracts assembly can name middleware without referencing its code:
The order, outer to inner, is: global middleware in registration order, then attribute aliases by Order (ties by alias name), then fluent per-job middleware, then the handler. Middleware is scoped and resolved fresh for every run. A worker that sees an alias nobody registered refuses to start (unknown_middleware_alias); a producer-only app does not check aliases.
Middleware cannot extend leases, reset budgets or finish a job itself. Those are the worker's job.
Next
- Retries and timeouts
- Testing: run handlers without a database.