Complete reference for all public types, methods, and properties in XpressWork.
- ServiceCollectionExtensions
- IBackgroundWorkSubmitter<TContext>
- IBackgroundWorkQueue<TContext>
- IBackgroundWorkScheduler<TContext>
- IBackgroundWorkScopeFactory<TContext>
- BackgroundWorkScopeFactoryBase<TContext>
- IBackgroundWorkContextAccessor<TContext>
- IWorkHandler<TWorkArguments>
- IHasOrderProperty
- BackgroundActionWorkArguments
- XpressWorkOptions
Extension methods for registering XpressWork services.
Registers all XpressWork services with a custom context type and scope factory.
public static IServiceCollection AddXpressWork<TContext, TScopeFactory>(
this IServiceCollection services,
Action<XpressWorkOptions>? configureOptions = null)
where TScopeFactory : class, IBackgroundWorkScopeFactory<TContext>Type Parameters:
TContext- The context type to capture and restoreTScopeFactory- Your implementation ofIBackgroundWorkScopeFactory<TContext>
Parameters:
services- The service collectionconfigureOptions- Optional action to configureXpressWorkOptions
Example:
services.AddXpressWork<MyAppContext, MyAppScopeFactory>(options =>
{
options.MaxQueueLength = 1000;
options.DrainOnShutdown = true;
});Registers XpressWork services without custom context (uses object? as context type).
public static IServiceCollection AddXpressWork(
this IServiceCollection services,
Action<XpressWorkOptions>? configureOptions = null)Example:
services.AddXpressWork();Registers the background work scheduler for rate-controlled work submission.
public static IServiceCollection AddXpressWorkScheduling<TContext>(
this IServiceCollection services)Type Parameters:
TContext- The context type (must match yourAddXpressWorkcall)
Example:
services.AddXpressWork<MyAppContext, MyAppScopeFactory>();
services.AddXpressWorkScheduling<MyAppContext>(); // Add scheduling supportNote: Call after
AddXpressWorkas scheduling depends on the queue being registered.
Registers a work handler for a specific work arguments type.
public static IServiceCollection AddWorkHandler<TWorkArguments, THandler>(
this IServiceCollection services)
where THandler : class, IWorkHandler<TWorkArguments>Example:
services.AddWorkHandler<SendEmailArgs, SendEmailHandler>();
services.AddWorkHandler<SendEmailArgs, LogEmailHandler>(); // Multiple handlers OKRegisters the built-in handler for delegate-based work.
public static IServiceCollection AddBackgroundActionWorkHandler(
this IServiceCollection services)Example:
services.AddBackgroundActionWorkHandler();Scoped service for submitting work to the background queue.
Lifetime: Scoped
Enqueues work arguments for background processing.
ValueTask<TaskCompletionSource> Enqueue(
object workArguments,
bool awaited = true,
CancellationToken cancellationToken = default)Parameters:
workArguments- The work to process (must not contain scoped services)awaited- Iftrue, queue waits for this item before processing nextcancellationToken- Cancellation token for the enqueue operation
Returns: A TaskCompletionSource to track completion
Example:
var completion = await _submitter.Enqueue(new SendEmailArgs { To = "test@example.com" });
await completion.Task; // Wait for completionImmediately executes work, bypassing the queue.
Task Invoke(
object workArguments,
CancellationToken cancellationToken = default)Example:
await _submitter.Invoke(new UrgentWorkArgs { ... });Singleton hosted service that processes work items.
Lifetime: Singleton (IHostedService)
| Property | Type | Description |
|---|---|---|
QueueLength |
int |
Number of items waiting in the queue |
Enqueues work with explicit context.
ValueTask<TaskCompletionSource> Enqueue(
object workArguments,
TContext? context = default,
bool awaited = true,
CancellationToken cancellationToken = default)Immediately executes work with explicit context.
Task Invoke(
object workArguments,
TContext? context = default,
CancellationToken cancellationToken = default)Note: Prefer using
IBackgroundWorkSubmitter<TContext>instead ofIBackgroundWorkQueue<TContext>directly, as the submitter handles context capture automatically.
Singleton hosted service for rate-controlled work submission.
Lifetime: Singleton (IHostedService)
Schedules work using debounce semantics - executes after a period of inactivity.
ValueTask Debounce<T>(
TimeSpan interval,
Guid key,
T workArgument,
TContext? context = default,
bool awaited = true,
bool preserveDispatcher = false,
CancellationToken cancellationToken = default)Parameters:
interval- The debounce interval. Work executes only after this duration passes with no new calls.key- A unique key to identify this debounced work. Multiple calls with the same key reset the timer.workArgument- The work arguments to enqueue when the debounce period expires.context- Optional context. If not provided, context may be captured from current scope.awaited- Iftrue, the queue processor will await this item's completion.preserveDispatcher- Iftrue, preserves the dispatcher entry after execution.cancellationToken- A cancellation token.
Example:
// Save only after 500ms of inactivity
await scheduler.Debounce(
TimeSpan.FromMilliseconds(500),
userId,
new SavePreferencesArgs { UserId = userId });Schedules work using throttle semantics - executes at most once per interval.
ValueTask Throttle<T>(
TimeSpan interval,
Guid key,
T workArgument,
TContext? context = default,
bool awaited = true,
bool preserveDispatcher = false,
CancellationToken cancellationToken = default)Parameters:
interval- The throttle interval. After execution, subsequent calls are delayed until this interval expires.key- A unique key to identify this throttled work stream.workArgument- The work arguments.context- Optional context.awaited- Iftrue, the queue processor will await this item's completion.preserveDispatcher- Iftrue, preserves the dispatcher entry after execution.cancellationToken- A cancellation token.
Example:
// Process at most once per second
await scheduler.Throttle(
TimeSpan.FromSeconds(1),
sensorId,
new ProcessSensorArgs { SensorId = sensorId, Value = reading });Executes work immediately on first call, then applies debounce semantics.
ValueTask RunOnceThenDebounce<T>(
TimeSpan interval,
Guid key,
T workArgument,
TContext? context = default,
bool awaited = true,
CancellationToken cancellationToken = default)Example:
// First call executes immediately, subsequent calls debounce
await scheduler.RunOnceThenDebounce(
TimeSpan.FromMilliseconds(500),
entityId,
new SaveEntityArgs { EntityId = entityId });Executes work immediately on first call, then applies throttle semantics.
ValueTask RunOnceThenThrottle<T>(
TimeSpan interval,
Guid key,
T workArgument,
TContext? context = default,
bool awaited = true,
CancellationToken cancellationToken = default)Cancels a pending scheduled work item by key.
(Func<IEnumerable<object>?, Task>? action, List<object>? accumulatedParameters) Cancel<T>(
Guid key,
bool preserveDispatcher = false)Parameters:
key- The unique key identifying the scheduled work.preserveDispatcher- Iftrue, preserves the dispatcher entry for future scheduling.
Returns: A tuple containing the cancelled action and any accumulated parameters. Both are null if no pending work was found.
Example:
var (action, params) = scheduler.Cancel<SavePreferencesArgs>(userId);
if (action != null)
{
Console.WriteLine("Cancelled pending save operation");
}Cancels a pending scheduled work item by key and argument type.
(Func<IEnumerable<object>?, Task>? action, List<object>? accumulatedParameters) Cancel(
Guid key,
Type argumentType,
bool preserveDispatcher = false)Immediately flushes all pending scheduled work to the queue.
Task FlushAsync(CancellationToken cancellationToken = default)Remarks:
- Enqueues all pending debounced and throttled work immediately.
- Waits for all enqueued work to complete.
- May execute multiple flushing rounds if handlers enqueue new rate-controlled work.
- Automatically called during graceful shutdown.
Example:
// Force all pending work to execute now
await scheduler.FlushAsync();Clears all pending scheduled work without executing it.
void Clear()Remarks:
- All pending work is discarded.
- Use when you want to cancel all scheduled work without execution.
Singleton service for capturing context and creating worker scopes.
Lifetime: Singleton
Captures context from a scoped provider.
TContext CaptureScopeContext(IServiceProvider scopedProvider)Parameters:
scopedProvider- The scoped service provider (e.g., from HTTP request)
Returns: The captured context data
Creates a new scope without context.
AsyncServiceScope CreateScope()Creates a new scope with rehydrated context.
AsyncServiceScope CreateScope(TContext context)Parameters:
context- The context to rehydrate
Returns: A new scope with context available via IBackgroundWorkContextAccessor<TContext>
Populates context in a worker scope.
void RehydrateBackgroundScope(IServiceProvider scopedProvider, TContext context)Abstract base class for implementing scope factories.
protected BackgroundWorkScopeFactoryBase(
ILogger<BackgroundWorkScopeFactoryBase<TContext>> logger,
IServiceScopeFactory serviceScopeFactory)Override these in your implementation:
public abstract TContext CaptureScopeContext(IServiceProvider scopedProvider);
public abstract void RehydrateBackgroundScope(IServiceProvider scopedProvider, TContext context);Example Implementation:
public class MyAppScopeFactory : BackgroundWorkScopeFactoryBase<MyAppContext>
{
public MyAppScopeFactory(
ILogger<BackgroundWorkScopeFactoryBase<MyAppContext>> logger,
IServiceScopeFactory serviceScopeFactory)
: base(logger, serviceScopeFactory) { }
public override MyAppContext CaptureScopeContext(IServiceProvider scopedProvider)
{
var http = scopedProvider.GetService<IHttpContextAccessor>()?.HttpContext;
return new MyAppContext
{
UserId = http?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value
};
}
public override void RehydrateBackgroundScope(IServiceProvider scopedProvider, MyAppContext context)
{
scopedProvider.GetRequiredService<IBackgroundWorkContextAccessor<MyAppContext>>().Context = context;
}
}Scoped accessor for the current context.
Lifetime: Scoped
| Property | Type | Description |
|---|---|---|
Context |
TContext? |
The current context, or null if not set |
Example:
public class MyHandler : IWorkHandler<MyArgs>
{
private readonly IBackgroundWorkContextAccessor<MyAppContext> _accessor;
public MyHandler(IBackgroundWorkContextAccessor<MyAppContext> accessor)
{
_accessor = accessor;
}
public Task DoWork(MyArgs args, CancellationToken ct)
{
var userId = _accessor.Context?.UserId;
// ...
}
}Interface for work handlers.
Lifetime: Scoped
| Property | Type | Description |
|---|---|---|
Order |
int |
Execution order (lower runs first) |
Processes the work arguments.
Task DoWork(TWorkArguments workArguments, CancellationToken cancellationToken = default)Marker interface for ordered execution.
| Property | Type | Description |
|---|---|---|
Order |
int |
Execution order (lower runs first) |
Work arguments for delegate-based work.
public BackgroundActionWorkArguments(
Func<IServiceProvider, CancellationToken, Task> Action,
string Name,
string? SpanId = null)Parameters:
Action- The delegate to execute (receives worker scope provider)Name- Display name for loggingSpanId- Optional span ID for tracing
| Property | Type | Description |
|---|---|---|
Action |
Func<IServiceProvider, CancellationToken, Task> |
The work delegate |
Name |
string |
Display name |
SpanId |
string? |
Optional tracing span ID |
Example:
await _submitter.Enqueue(new BackgroundActionWorkArguments(
async (sp, ct) =>
{
var service = sp.GetRequiredService<IMyService>();
await service.DoWorkAsync(ct);
},
Name: "ProcessData",
SpanId: Activity.Current?.SpanId.ToString()));Configuration options for XpressWork.
| Property | Type | Default | Description |
|---|---|---|---|
MaxQueueLength |
int |
0 |
Max queue size (0 = unbounded) |
DrainOnShutdown |
bool |
false |
Process remaining items on shutdown |
HandlerErrorMode |
HandlerErrorMode |
StopOnFirst |
Error handling policy |
EnableDiagnostics |
bool |
false |
Enable verbose logging |
ShutdownTimeout |
TimeSpan |
30s |
Max wait time for graceful shutdown |
| Value | Description |
|---|---|
StopOnFirst |
Stop handler chain on first exception |
ContinueOnError |
Execute all handlers, aggregate exceptions |
Example:
services.AddXpressWork<MyContext, MyScopeFactory>(options =>
{
options.MaxQueueLength = 1000;
options.DrainOnShutdown = true;
options.HandlerErrorMode = HandlerErrorMode.ContinueOnError;
options.EnableDiagnostics = true;
options.ShutdownTimeout = TimeSpan.FromMinutes(1);
});