To put it mildly, what these engineers are proposing, is pure cancer.
In an effort to make the data repository and business services thread-safe by making every one of them singletons, the engineers have actually created vulnerabilities where threading problems and side effects can be easily introduced into the application.
Using AddScoped each request get's it's own copy of the service, but using AddSingleton the DI framework creates a single instance of the service and shares it across all requests. This means that each HTTP request that comes in will use the same instance of the service, and any data stored in that instance will be accessible to all requests, thus creating thread safety issues where otherwise there would have been none.
Using this approach means every function, library, and service utilized throughout the application must be evaluated and tested for thread safety and side effects, including race conditions and deadlock scenarios. Additionally if the repository or business services even share any transient services, then thread safety can become an issue even between separate services.
Since singletons persist in memory for the lifetime of the application, this approach has also made the application susceptible to memory leaks, now it is every developers responsibility to make sure all objects are out of reference and cleaned up before exiting or they may persist in memory, growing with every request.
This approach has also decreased the applications testability and reliability, as testing multi-thread scenarios is more complex and challenging.
From James Michael Hickey's blog post discussing how singletons utilizing transitive services can allow completely different HTTP requests to add, remove and modify elements of each others collections:
Singletons + Transitive Services
A singleton, again, lives “forever”.
It’s always the same instance.
Transitive services, on the other hand, are always a different
instance when requested - or resolved.
So here’s an interesting question: When a singleton depends on a
transitive dependency how long does the transitive dependency live?
The answer is forever. More specifically, as long as it’s parent
Since the singleton lives forever so will all of it’s child objects
that it references.
This isn’t necessarily bad. But it could introduce weird issues when
you don’t understand what this setup implies.
Thread-Safety Perhaps you have a transitive service - let’s call it
ListService that isn’t thread-safe.
ListService has a list of stuff and exposes methods to Add and Remove
Now, you started using ListService inside of a singleton as a
That singleton will be re-used everywhere. That means, on every HTTP
Request. Which implies on many many different threads.
Since the singleton accesses/uses ListService, and ListService isn’t
thread-safe - big problems!
A strong case should be made for using a singleton, each case should be evaluated thoroughly before making the decision, there should be compelling reasons for its usage. A robust and well-supported argument must be presented in favor of utilizing a singleton in any given scenario.
If you want to constantly be putting out fires and want your application to be the talk of the organization, then by all means, use this approach.
Scopedthen it may not work correctly when you change the deployment to
Singleton. Those bugs can be very subtle.