Skip to main content
Version: 1.2

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​

PropertyTypeMeaning
JobIdBackgroundJobId (readonly record struct wrapping string)Unique identifier for this specific execution instance
RecurringJobIdstring?The Hangfire recurring-job ID this execution belongs to (e.g. "campaign-cleanup-pipeline"), null for a one-off fire-and-forget execution
TenantIdGuid?Which tenant this execution is scoped to; null means host-level (all-tenant) — see §8 — Tenant Context
UserIdGuid?The user who triggered the execution, if any (e.g. a manually-triggered job); null for system-scheduled recurring executions
Queuestring?The Hangfire queue this execution runs on, if a non-default queue is used
TriggerTypeBackgroundJobTriggerTypeFireAndForget=1, Delayed=2, Recurring=3, Continuation=4
CorrelationIdstring?An operator-facing tracing identifier, threaded through logs for this execution
PriorityBackgroundJobPriorityLow=1, Normal=2, High=3, Critical=4
CancellationTokenCancellationTokenThe 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.