DI021
Non-Thread-Safe Service Shared Across Concurrent Handler Invocations
A documented non-thread-safe service (EF Core DbContext and derived contexts, DbConnection/DbCommand/DbTransaction/DbDataReader and their interfaces, IDbContextTransaction, HttpContext) created or resolved once and then captured — through a field, a closure over an outer local, or an enclosing method parameter — into a handler that a framework invokes concurrently: ServiceBusProcessor/ServiceBusSessionProcessor message and error handlers, EventProcessorClient event handlers, RabbitMQ EventingBasicConsumer.Received/AsyncEventingBasicConsumer.Received/ReceivedAsync consumer handlers (instance-correlated through the consumer's own factory/connection/channel chain: proven ConsumerDispatchConcurrency above 1 warns, proven 1 or a fresh default factory stays silent, untraceable chains stay config-gated; fallback constants must bind to the real RabbitMQ property), System.Threading.Timer callbacks with a finite period, System.Timers.Timer.Elapsed, Parallel.For/ForEach/ForEachAsync/Invoke bodies, PLINQ ForAll bodies (sequential only when WithDegreeOfParallelism(1) is proven on the query chain), TPL Dataflow ActionBlock/TransformBlock/TransformManyBlock delegates (sequential by default; reported when MaxDegreeOfParallelism is provably above 1, config-gated DI022 when the options are unprovable), and EventProcessor<TPartition> batch/error overrides (the override body is the handler; partitions run concurrently). Resolving from a long-lived scope captured from outside the handler is reported too — it hands the same instance to every concurrent invocation. Both generic requests and exact framework non-generic IServiceProvider.GetService(typeof(T)) / GetRequiredService(typeof(T)) requests participate; direct-static calls bind the provider by declared parameter only for exact framework extension containers, and concrete provider implementations bind the System.Type contract parameter regardless of its source name. Generic service-looking calls must bind to the exact framework extension symbol; same-named user-defined generic helpers remain silent. The adversarial boundary is recorded in docs/adversarial/DI021.md. Built-in identity, reference, and boxing conversions remain transparent while tracing provider origins, preserving coverage for captured value-type and IServiceProvider-constrained providers; cyclic constraint graphs in temporarily invalid source are bounded by symbol identity. Runtime Type values, user-defined conversions on the requested type or provider receiver, and user-defined same-named helpers remain conservative and silent.
Warning Default severity · Code fix: Yes