HttpContext is not thread-safe and is valid only for the duration of the request.
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); |