DI003

Captive Dependency

singleton services capturing scoped or transient dependencies, including constructor injection and high-confidence factory paths such as inline delegates, method-group factories, keyed resolutions, and `ActivatorUtilities.CreateInstance(...)` without explicit constructor arguments.

Default severity: Warning · Code fix: Yes

Why it matters

lifetime mismatch can produce stale state, leaks, and thread-safety defects.

If one pupil keeps the shared class scissors all term, nobody else can use them when needed.

README problem example

services.AddScoped<IScopedService, ScopedService>();
services.AddSingleton<ISingletonService, SingletonService>();

public sealed class SingletonService : ISingletonService
{
    public SingletonService(IScopedService scoped) { }
}

README better pattern

services.AddScoped<ISingletonService, SingletonService>();

// or keep singleton and create scopes inside operations
public sealed class SingletonService : ISingletonService
{
    private readonly IServiceScopeFactory _scopeFactory;

    public SingletonService(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public void Run()
    {
        using var scope = _scopeFactory.CreateScope();
        var scoped = scope.ServiceProvider.GetRequiredService<IScopedService>();
        scoped.DoWork();
    }
}

Yes. Rewrites explicit registration lifetimes when the registration syntax is local and unambiguous (for example `AddSingleton`, keyed `AddKeyedSingleton`, and supported `ServiceDescriptor` forms).

Repo sample extraction

Examples pulled from the sample app

Open full sample file

Sample app warning case

public class BadSingletonWithScopedDependency
{
    private readonly IScopedService _scopedService;

    // DI003: Singleton 'BadSingletonWithScopedDependency' captures scoped dependency 'IScopedService'
    public BadSingletonWithScopedDependency(IScopedService scopedService)
    {
        _scopedService = scopedService;
    }

    public void DoWork() => _scopedService.DoWork();
}

Sample app safe pattern

public class GoodSingletonWithScopeFactory
{
    private readonly IServiceScopeFactory _scopeFactory;

    public GoodSingletonWithScopeFactory(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public void DoWork()
    {
#pragma warning disable DI007 // Factory pattern - IServiceScopeFactory is allowed to resolve services
        using var scope = _scopeFactory.CreateScope();
        var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();
        scopedService.DoWork();
#pragma warning restore DI007
    }
}