A service lifetime tells the dependency-injection (DI) container when to reuse a registered service instance and when to create a new one. Choose the lifetime when registering the service, alongside its service type and implementation type. section 9.4 section 9.1
ASP.NET Core’s built-in container offers three lifetimes:
- Transient: create a new instance every time the service is requested, even if it is requested twice within one dependency graph. section 9.4
- Scoped: reuse one instance within a scope and create a different instance for another scope. Each ASP.NET Core web request has its own scope. section 9.4
- Singleton: reuse the same instance throughout the application, regardless of scope. section 9.4
Registering an email service
Registering IEmailSender with AddScoped<IEmailSender, EmailSender>() gives requests within one scope the same EmailSender instance. Registering MessageFactory with AddSingleton<MessageFactory>() instead makes its instance available for reuse across scopes. If you already have an object, passing it to AddSingleton registers that particular object; a scoped factory lambda can create an object when a new scoped instance is needed. section 9.1 section 9.2
Figure 9.6 The DI container uses two instances of DataContext for each request. Depending on the lifetime with which the DataContext type is registered, the container might create one, two, or four instances of DataContext.
Compare the DataContext instances used across requests. Changing its lifetime changes whether the handler and its Repository dependency receive matching or different instances. section 9.4
Lifetime choices matter beyond ordinary request handling. A long-lived service that captures a shorter-lived dependency can cause hard-to-debug problems. When resolving a scoped service manually outside a request, create a scope, resolve the service from that scope’s ServiceProvider, and dispose of the scope afterward. CreateAsyncScope() is generally preferred where available. section 9.4 section 9.5

