11. Execution Context
11.1 The contract
public interface IBackgroundJobExecutionContext
{
BackgroundJobId JobId { get; }
string? RecurringJobId { get; }
Guid? TenantId { get; }
Guid? UserId { get; }
string? Queue { get; }
BackgroundJobTriggerType TriggerType { get; }
string? CorrelationId { get; }
BackgroundJobPriority Priority { get; }
CancellationToken CancellationToken { get; }
}
Every property is read-only — a context is fully specified at construction and never mutated afterward.
11.2 Every property, explained
| Property | Type | Meaning |
|---|---|---|
JobId | BackgroundJobId (readonly record struct wrapping string) | Unique identifier for this specific execution instance |
RecurringJobId | string? | The Hangfire recurring-job ID this execution belongs to (e.g. "campaign-cleanup-pipeline"), null for a one-off fire-and-forget execution |
TenantId | Guid? | Which tenant this execution is scoped to; null means host-level (all-tenant) — see §8 — Tenant Context |
UserId | Guid? | The user who triggered the execution, if any (e.g. a manually-triggered job); null for system-scheduled recurring executions |
Queue | string? | The Hangfire queue this execution runs on, if a non-default queue is used |
TriggerType | BackgroundJobTriggerType | FireAndForget=1, Delayed=2, Recurring=3, Continuation=4 |
CorrelationId | string? | An operator-facing tracing identifier, threaded through logs for this execution |
Priority | BackgroundJobPriority | Low=1, Normal=2, High=3, Critical=4 |
CancellationToken | CancellationToken | The token propagated all the way from the Hangfire bridge into the job's RunAsync call |
11.3 The concrete implementation
BackgroundJobExecutionContext (public, sealed class, in Core) implements the interface. Its constructor
signature is frozen under the framework's LTS backward-compatibility policy — adding a required parameter to
it would be a breaking, MAJOR-version-only change:
public BackgroundJobExecutionContext(
BackgroundJobId jobId,
BackgroundJobTriggerType triggerType,
string? recurringJobId = null,
Guid? tenantId = null,
Guid? userId = null,
string? queue = null,
string? correlationId = null,
BackgroundJobPriority priority = BackgroundJobPriority.Normal,
CancellationToken cancellationToken = default)
Only jobId and triggerType are required — every other property has a sensible default, keeping the
constructor usable for the simplest case (a host-level, unscoped, normal-priority fire-and-forget job) while
still allowing every dimension to be specified explicitly.
11.4 Lifecycle
Creation: a BackgroundJobExecutionContext is constructed exactly once per execution, inside the
Hangfire bridge (the I{Job}PipelineJob implementation), immediately before the pipeline is invoked. It is
never reused across executions — even for the same recurring job, each tick gets a brand-new context with a
fresh JobId.
Ownership: the context is owned by whichever call constructed it (the bridge) for the duration of a
single RunAsync/ExecuteAsync/pipeline call chain. It flows by reference through
IBackgroundJobExecutionPipeline.RunAsync → IBackgroundJobExecutor.ExecuteAsync → the job's own
IBackgroundJob.RunAsync — every layer receives the same context instance, never a copy.
Disposal: the context implements no IDisposable/IAsyncDisposable — it is a plain immutable data
holder with no unmanaged resources of its own. It is simply discarded (garbage-collected) once the execution
chain returns.
11.5 Public API surface
Only the interface (IBackgroundJobExecutionContext, in Abstractions) and the one concrete implementation
(BackgroundJobExecutionContext, in Core) are public. There is no factory/builder type — callers construct
the concrete class directly. See §12.7 for a
complete worked construction example.