Welcome to my post!
Today I will discuss how you use the three important service lifetimes with dependency injection.
You may already be familiar with using the dependency injection SOLID pattern within .NET Core applications.
The ability to manage lifetimes of application services allows us to control how long a service instance is alive within the service container. Certain types of service instances that include HTTP client requests or data contexts require a longer lifetime as there are limits in the number of HTTP client connection ports or database connections that can be simultaneously created in the server hosting the application service.
The longer the service lifetimes of an instance, the need to manage concurrency access to the resource within the instance and prevent potential data corruption in the case of a data context, or in the case of an HTTP client an exhaustion of ports.
The Three Lifetime Scopes
The lifetime scopes are Singleton, Scoped and Transient.
Within a .NET Core application services are created with the three service collection extension methods AddSingleton(), AddScoped() and AddTransient().
When you first encounter these scopes, it can be confusing as to which lifetime to use within a .NET Core application, especially an application that is running as a service, such as a Web API. Using the wrong lifetime will lead your application to fail under certain scenarios, such as multi-user access to a service.
I will provide recommendations on what you can use in different application requirement contexts.
The Singleton Lifetime
With Singleton lifetime, the interface you want to instantiate will span the lifetime of the application.
The diagram below illustrates this:
A typical usage scenario is when you need to define a set of configurations that are visible to your application components (start up, controllers and classes).
With singleton calls, we want to retain the objects of the services that are injected into each service collection and only release these objects after the application is shutdown.
The Scoped Lifetime
With a Scoped service, the lifetime of the service instance that you want to instantiate will span the lifetime of the request or controller. Each request in the web application corresponds to each request that is sent to the web application. The diagram below illustrates this:
The diagram below illustrates this:
Typical usage scenarios are within web session user contexts and data access contexts ensuring isolation and uniqueness for security and concurrency reasons.
With scoped calls, we want to retain the objects of the services that are injected into each service collection and only release these objects after each web request goes out of scope.
The Transient Lifetime
With transient calls, we want to retain the objects within the context of the service instances that are injected into each service collection and only release these objects after each object goes out of scope within its service instance on disposal.
Summary of Recommended Usage Scenarios
Use this lifetime for instantiated user classes that will vary whenever they are created on demand within a class. This is clearly the most frequently used lifetime scope in an application.
Ideally stateless service classes would be ideal candidates for the transient lifetimes.
Examples would be data repository classes, service classes and so on.
Use this lifetime for web session user contexts and data access contexts will vary and require isolation for security and concurrency reasons.
Examples of usage would be in the following contexts:
- Data context
Persistence of data context connections during access to repository classes during a request.
- HTTP context
Context of the instantiated objects persist during a Web HTTP request (such as an API call).
Use this lifetime when you need to define a set of configurations that are visible to your application components (start up, controllers and classes), and expect the lifetime of the instantiated objects to persist during the lifetime of the application, then use the Singleton lifetime.
An example would be an application configuration class, whose properties are accessible to all classes instantiated within the application.
We will discuss how you can do this is a future blog.
A detailed treatment of service lifetimes is here (https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.0#service-lifetimes).
That’s all for today’s post.
I hope you have found this post useful and informative.
Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.