DI024
Hosted Service Creates Scope Outside Execution Loop
Two tiers. First, a BackgroundService.ExecuteAsync override or IHostedService/IHostedLifecycleService start method that creates an IServiceScope once before its long-running execution loop (while (!token.IsCancellationRequested), compound cancellation conditions, while (true), for (;;), System.Threading.PeriodicTimer.WaitForNextTickAsync loops (same-named custom timer methods do not qualify), and channel-consumer loops — await foreach over ChannelReader<T>.ReadAllAsync(...) or while (await reader.WaitToReadAsync(...)), including channel loops nested inside an outer cancellation loop when the scope is created per outer iteration but spans the unbounded inner drain; ConfigureAwait(...)/WithCancellation(...) wrappers on any of the awaited shapes are peeled before gating) and uses it inside the loop — directly, through a service resolved from it before the loop, or through a provider alias local (var sp = scope.ServiceProvider;) used inside the loop. The same helper-local analysis follows one-hop, directly invoked private helpers declared on the same type; field candidates stay confined to true hosted entry points. Generic resolutions and the framework's direct-typeof(T) non-generic GetService/GetRequiredService forms participate, including keyed GetKeyedService/GetRequiredKeyedService calls whose service key is compile-time known, plus casted and null-forgiving results; runtime Type values, dynamic keys, and user-defined same-named methods remain unproven. Compound conditions are evaluated conservatively: nested ! operators are reduced by polarity, every && operand must be long-running because any operand can bound the loop, while one long-running || operand is sufficient; negated cancellation combinations use De Morgan semantics. Declare-then-assign locals (IServiceScope? scope = null; try { scope = factory.CreateScope(); while (...) ... } finally { scope?.Dispose(); } — the try/finally ownership pattern) qualify via their pre-loop assignment: the last direct pre-loop write wins, so a creation makes the candidate and a null/default clear (or an unrecognized value) kills it. Second, a service whose effective registration is provably scoped, resolved once before the loop from any provider and reused across iterations. Both tiers also cover fields: a scope (or resolved service) stored in a field qualifies when every assignment to the field is the expected shape and every assignment site is a field initializer, a constructor, or a hosted execution method (BackgroundService.StartAsync overrides included); partial types are analyzed across all declarations. Reported at the CreateScope/CreateAsyncScope or service-resolution call with the loop as an additional location.
Warning Default severity · Code fix: No