Notice: Function WP_Scripts::add was called incorrectly. The script with the handle "markdown-renderer" was enqueued with dependencies that are not registered: mermaid-js, prism-core. Please see Debugging in WordPress for more information. (This message was added in version 6.9.1.) in /home/dataadl/www/wp-includes/functions.php on line 6131

Dependency Injection in .NET: Service Lifetimes

Using the wrong DI lifetime is the #1 cause of concurrency bugs in ASP.NET Core. We revisit Singleton, Scoped, and Transient with a focus on thread safety.

The Three Lifetimes

LifetimeCreated…Thread Safety
TransientEvery time requestedSafe (Instance per usage)
ScopedOnce per HTTP RequestSafe (Single thread per request)
SingletonOnce per App LifetimeUNSAFE (Shared across all requests)

The Captive Dependency Problem

Injecting a Scoped service into a Singleton service is a fatal error. The Scoped service will stay alive forever (captured), possibly holding onto a DbConnection.

// ❌ BAD
public class SingletonService
{
    private readonly ScopedService _scoped; // Captured!
    public SingletonService(ScopedService scoped) => _scoped = scoped;
}

// ✅ GOOD (IServiceScopeFactory)
public class SingletonService
{
    private readonly IServiceScopeFactory _scopeFactory;
    
    public void DoWork()
    {
        using var scope = _scopeFactory.CreateScope();
        var scoped = scope.ServiceProvider.GetRequiredService<ScopedService>();
        scoped.DoThing();
    }
}

Key Takeaways

  • EF Core `DbContext` is **Scoped**. Never inject it into a Singleton.
  • Use `ValidateScopes = true` in development to catch these bugs early.

Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.