HttpContext, scoped services and AsyncLocal across threads in ASP.NET Core: what is safe

C# Async & Multithreading November 13, 2026 csharp

HttpContext is not thread-safe and is valid only for the duration of the request.

csharp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ======================================================================
// Q: Can you use HttpContext, scoped services or AsyncLocal from other threads?
//
// - HttpContext is not thread-safe and is valid only for the duration of the
//   request. Afterwards it is reused, so a background task would read another
//   request's data. Copy what you need (userId, correlationId) into plain
//   variables.
// - Parallel tasks on the same scoped service (e.g. DbContext, Q63): no.
// - AsyncLocal<T>: a value that follows the logical flow through awaits. This is
//   how Activity.Current, logging scopes and IHttpContextAccessor work. Changes
//   made inside a child task don't flow back to the parent. Use it rarely,
//   because it is hidden state.
// - There is no async constructor. Alternatives: a static CreateAsync factory,
//   initialization in IHostedService.StartAsync, or Lazy<Task<T>>.
// ======================================================================

// ✗ After the request ends, the HttpContext belongs to another request (or has been disposed)
_ = Task.Run(async () => await audit.LogAsync(HttpContext.User.Identity!.Name, orderId));

// ✓ Copy the values and send them to a queue / outbox
var user = HttpContext.User.Identity!.Name;
await auditQueue.Writer.WriteAsync(new AuditEntry(user, orderId), ct);