What’s an object’s lifetime? – Dependency Injection

What’s an object’s lifetime? – Dependency Injection

When we create an instance manually, using the new keyword, we create a hold on that object; we know when we create it and when its life ends. That’s the lifetime of the object.Of course, using the new keyword leaves no chance to control these objects from the outside, enhance them, intercept them, or swap them for another implementation—as covered in the preceding Code smell – Control Freak section.

.NET object lifetime

With dependency injection, we need to forget about controlling objects and start to think about using dependencies, or more explicitly, depending on their interfaces. In ASP.NET Core, there are three possible lifetimes to choose from:

LifetimeDescription
TransientThe container creates a new instance every time.
ScopedThe container creates an instance per HTTP request and reuses it. In some rare cases, we can also create custom scopes.
SingletonThe container creates a single instance of that dependency and always reuses that unique object.

 Table 8.1: objects lifetime description

We can now manage our volatile dependencies using one of those three scopes. Here are some questions to help you choose:

  • Do I need a single instance of my dependency? Yes? Use the singleton lifetime.
  • Do I need a single instance of my dependency shared over an HTTP request? Yes? Use the scoped lifetime.
  • Do I need a new instance of my dependency every time? Yes? Use the transient lifetime.

A general approach to object lifetime is to design the components to be singletons. When impossible, we go for scoped. When scoped is also impossible, go for transient. This way, we maximize instance reuse, lower the overhead of creating objects, lower the memory cost of keeping those objects in memory, and lower the amount of garbage collection needed to remove unused instances.

For example, we can pick singleton mindlessly for stateless objects, which are the easiest to maintain and less likely to break.

For stateful objects, where multiple consumers use the same instance, we must ensure the object is thread-safe if the lifetime is singleton or scoped because multiple consumers could try to access it simultaneously.

One essential aspect to consider when choosing a lifetime is the consumers of stateful objects. For example, if we load data related to the current user, we must ensure that data do not leak to other users. To do so, we can define the lifetime of that object to scoped, which is limited to a single HTTP request. If we don’t want to reuse that state between multiple consumers, we can choose a transient lifetime to ensure every consumer gets their own instance.

How does that translate into code? .NET offers multiple extension methods to help us configure the lifetimes of our objects, like AddTransient, AddScoped, and AddSingleton, which explicitly state their lifetimes.

We use the built-in container throughout the book with many of its registration methods, so you should grow familiar with it very quickly. It has good discoverability, so you can explore the possibilities using IntelliSense while writing code or reading the documentation.

Next, we use those methods and explore how to register dependencies with the container.

Leave a Reply

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