Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which dependency injection lifetime should be preferred?

For a REST api, that has no dependencies between requests and is using ASP.NET Core DI.

I have heard conflicting arguments when choosing the most appropriate:

  • Prefer transient and then scoped and avoid singleton due to memory and multi-threading issues and also because singletons should not be injected with other lifetimes due to captive dependencies
  • Prefer singletons to save time instantiating objects and prevent opening multiple connections

I understand they work differently but is there a 'go-to' lifetime? Should it start with 'transient' and move to others as required?

Are there any investigations proving the instantiation time saved by singleton is actually relevant?

like image 462
Perbert Avatar asked Mar 06 '20 17:03

Perbert


People also ask

What is dependency injection lifetime?

The dependency injection container keeps track of all instances of the created services, and they are disposed of or released for garbage collector once their lifetime has ended.

Should services be singleton or transient?

Use Transient lifetime for the lightweight service with little or no state. Scoped services service is the better option when you want to maintain state within a request. Singletons are created only once and not destroyed until the end of the Application. Any memory leaks in these services will build up over time.

What is the difference between Addscoped and Addtransient and Addsingleton?

Show activity on this post. Singleton is a single instance for the lifetime of the application domain. Scoped is a single instance for the duration of the scoped request, which means per HTTP request in ASP.NET. Transient is a single instance per code request.


2 Answers

I'm not going to say there's a right or wrong way here, but I'll share my personal experience.

I've tried both ways, and ended up finding that it's best to start with transient and then expand the scope as necessary.

Think in terms of the Single Responsibility Principle and Reasons for Change. The reason that your service's lifetime scope may need to change will likely be linked to a change you make to the service implementation itself. When that Reason happens, you want to only need to change a single class.

If you have a service that needs to be longer-lived than most of your other services, all you have to do is make it longer-lived and inject factories for any shorter-lived dependencies into it.

On the other hand, if you have a class that needs to be shorter-lived than most of your other services, you'll end up having to inject it as a factory into all the longer-lived services. So one Reason for Change impacts code all over your code base.

Of course, this is assuming you're doing things correctly: avoiding captive dependencies and remembering to make non-thread-safe services transient. I find it's far easier to remember which services need to be long-lived and override the defaults in that direction, than it is to remember which services shouldn't be long-lived and override the defaults that way. And I've found that failure to do the latter leads to much more insidious bugs that are harder to notice and result in greater damage.

With regards to performance: I haven't done performance testing with the ASP.NET Core built-in DI framework, but I've found that SimpleInjector is fast enough that the time and memory overhead of creating new instances of services is trivial in comparison with the other work my app does (like querying databases).

With regards to preventing opening multiple connections: SQL Server connections are pooled automatically so the cost to creating a new SqlConnection() and disposing it several times in a request is generally pretty trivial.

like image 74
StriplingWarrior Avatar answered Oct 07 '22 13:10

StriplingWarrior


Should it start with 'transient' and move to others as required?

Yep. Transient services are simpler to implement correctly, and simply instantiating an object every time a dependency is resolved would normally not cause a significant performance problem.

like image 26
David Browne - Microsoft Avatar answered Oct 07 '22 12:10

David Browne - Microsoft