Summary
Add ExecuteAllInContext — an IContextManager extension that runs a list of async delegates inside one logical context scope, with per-task fork-join isolation and correct ExecutionContext/AsyncLocal flow.
Goal
Provide a safe, first-party replacement for a hand-rolled array of tasks + Task.WhenAll, so context metadata (trace/tenant/correlation) never leaks or is lost across parallel branches.
Problem
Bare Task.WhenAll does not guarantee that tasks launched in a loop keep the library's ambient context, especially under SynchronizationContext configurations. The current ContextManager only executes single actions, so there is no library-level fork-join primitive.
Scope
Internal Loop Reference
vartasks=delegates.Select(work =>Task.Run(async()=>{usingvarscope=manager.BeginContext(snapshot);try{awaitwork();}catch(Exceptionex){Log(ex,snapshot.CorrelationId);}}));awaitTask.WhenAll(tasks);Design Expectations
- Isolation by default: child mutations never leak to siblings or the parent thread.
- Deterministic lifetime tied to
Task.WhenAll duration. - No global lock: the method owns no shared/global semaphore (resource control lives in the concurrency issue).
- Resource ownership stays with the caller.
- Child writes never propagate back unless an explicit aggregating adapter is used (Join discards).
Acceptance Criteria
Non-Goals
- No
MaxConcurrency in this issue — that is the separate ExecutionOptions concurrency issue. - No ordered-result preservation, no partial-success (
TryEach), no graph orchestration here — separate issues. - No blocking/
Parallel.ForEach — async-only.
Summary
Add
ExecuteAllInContext— anIContextManagerextension that runs a list of async delegates inside one logical context scope, with per-task fork-join isolation and correctExecutionContext/AsyncLocalflow.Goal
Provide a safe, first-party replacement for a hand-rolled array of tasks +
Task.WhenAll, so context metadata (trace/tenant/correlation) never leaks or is lost across parallel branches.Problem
Bare
Task.WhenAlldoes not guarantee that tasks launched in a loop keep the library's ambient context, especially underSynchronizationContextconfigurations. The currentContextManageronly executes single actions, so there is no library-level fork-join primitive.Scope
IContextManager<C>.ExecuteAllInContext(C ctx, params Func<Task>[] work)extension.manager.ExecuteInContext(snapshot, work)with a correctExecutionContext.Task.WhenAll; task scopes disposed deterministically when the batch completes.ContextLifecycleEventKind.Started/Completedaround each branch (per [Context]: add ActivityObserver for tracing context execution #11 event contract).Internal Loop Reference
Design Expectations
Task.WhenAllduration.Acceptance Criteria
Non-Goals
MaxConcurrencyin this issue — that is the separateExecutionOptionsconcurrency issue.TryEach), no graph orchestration here — separate issues.Parallel.ForEach— async-only.